diff options
176 files changed, 10565 insertions, 5258 deletions
@@ -1,2 +1,2 @@ To view a list of known bugs, or to enter a bug report, please use -Pandoc's issue tracker: <http://code.google.com/p/pandoc/issues/list> +Pandoc's issue tracker: <https://github.com/jgm/pandoc/issues> diff --git a/Benchmark.hs b/Benchmark.hs deleted file mode 100644 index 1fd787945..000000000 --- a/Benchmark.hs +++ /dev/null @@ -1,45 +0,0 @@ -import Text.Pandoc -import Text.Pandoc.Shared (readDataFile, normalize) -import Criterion.Main -import Data.List (isSuffixOf) -import Text.JSON.Generic - -readerBench :: Pandoc - -> (String, ParserState -> String -> Pandoc) - -> Benchmark -readerBench doc (name, reader) = - let writer = case lookup name writers of - Just w -> w - Nothing -> error $ "Could not find writer for " ++ name - inp = writer defaultWriterOptions{ writerWrapText = True - , writerLiterateHaskell = - "+lhs" `isSuffixOf` name } doc - -- we compute the length to force full evaluation - getLength (Pandoc (Meta a b c) d) = - length a + length b + length c + length d - in bench (name ++ " reader") $ whnf (getLength . - reader defaultParserState{ stateSmart = True - , stateStandalone = True - , stateLiterateHaskell = - "+lhs" `isSuffixOf` name }) inp - -writerBench :: Pandoc - -> (String, WriterOptions -> Pandoc -> String) - -> Benchmark -writerBench doc (name, writer) = bench (name ++ " writer") $ nf - (writer defaultWriterOptions{ - writerWrapText = True - , writerLiterateHaskell = "+lhs" `isSuffixOf` name }) doc - -normalizeBench :: Pandoc -> [Benchmark] -normalizeBench doc = [ bench "normalize - with" $ nf (encodeJSON . normalize) doc - , bench "normalize - without" $ nf encodeJSON doc - ] - -main = do - inp <- readDataFile (Just ".") "README" - let ps = defaultParserState{ stateSmart = True } - let doc = readMarkdown ps inp - let readerBs = map (readerBench doc) readers - defaultMain $ map (writerBench doc) writers ++ readerBs ++ normalizeBench doc - @@ -1,5 +1,5 @@ Pandoc -Copyright (C) 2006-2012 John MacFarlane <jgm at berkeley dot edu> +Copyright (C) 2006-2013 John MacFarlane <jgm at berkeley dot edu> This code is released under the [GPL], version 2 or later: @@ -62,15 +62,8 @@ you will need [zip-archive], [blaze-html], and [highlighting-kate]. preceded by a `-` (to force the flag to `false`), and separated by spaces. Pandoc's flags include: - - `executable`: build the pandoc executable (default yes) - - `library`: build the pandoc library (default yes) - - So, for example, - - --flags="-executable" - - tells Cabal to build the library but not the executables, - and to compile with syntax highlighting support. + - `blaze_html_0_5`: Use blaze-html >= 0.5 (default yes) + - `embed_data_files`: embed all data files into the binary (default no) 3. Build: @@ -94,37 +87,19 @@ you will need [zip-archive], [blaze-html], and [highlighting-kate]. generate a script that can be run to register the package at install time. -Creating a relocatable Windows binary -------------------------------------- +Creating a relocatable binary +----------------------------- -On Windows it is possible to compile pandoc such that it -(and its data files) are "relocatable." You can put the relocatable -binary in any directory (even on a USB drive), and it will look for its -data files there. +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. cabal install --flags="embed_data_files" citeproc-hs - cabal install --flags="executable -library" --datasubdir= - -You can find `pandoc.exe` in `dist/build/pandoc`. Copy this wherever -you please, and copy the following data files to the same place: - - README - COPYRIGHT - COPYING - reference.odt - reference.docx - epub.css - default.csl - templates/ - data/ - s5/ - slidy/ - slideous/ - dzslides/ - pcre-license.txt - pcre3.dll - -This is essentially what the binary installer does. + cabal configure --flags="embed_data_files" + cabal build + +You can find the pandoc executable in `dist/build/pandoc`. Copy this wherever +you please. [zip-archive]: http://hackage.haskell.org/package/zip-archive [highlighting-kate]: http://hackage.haskell.org/package/highlighting-kate @@ -136,9 +111,9 @@ Running tests ------------- Pandoc comes with an automated test suite integrated to cabal. -To enable the tests, compile pandoc with the `--enable-tests` option: +To build the tests: - cabal install --enable-tests + cabal configure --enable-tests && cabal build Note: If you obtained the source via git, you should first do @@ -151,11 +126,35 @@ To run the tests: cabal test +To run particular tests (pattern-matching on their names), use +the `-t` option: + + cabal test --test-options='-t markdown' + 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 -`src/test-pandoc.hs`. If you are adding a new reader or writer, it is +`tests/test-pandoc.hs`. If you are adding a new reader or writer, it is probably easiest to add some data files to the `tests` directory, and -modify `src/Tests/Old.hs`. Otherwise, it is better to modify the module -under the `src/Tests` hierarchy corresponding to the pandoc module you +modify `tests/Tests/Old.hs`. Otherwise, it is better to modify the module +under the `tests/Tests` hierarchy corresponding to the pandoc module you are changing. +Running benchmarks +------------------ + +To build the benchmarks: + + cabal configure --enable-benchmarks && cabal build + +To run the benchmarks: + + cabal 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' + @@ -1,6 +1,6 @@ % Pandoc User's Guide % John MacFarlane -% January 27, 2012 +% January 19, 2013 Synopsis ======== @@ -13,21 +13,22 @@ Description Pandoc is a [Haskell] library for converting from one markup format to another, and a command-line tool that uses this library. It can read [markdown] and (subsets of) [Textile], [reStructuredText], [HTML], -[LaTeX], and [DocBook XML]; and it can write plain text, [markdown], -[reStructuredText], [XHTML], [HTML 5], [LaTeX] (including [beamer] -slide shows), [ConTeXt], [RTF], [DocBook XML], [OpenDocument XML], -[ODT], [Word docx], [GNU Texinfo], [MediaWiki markup], [EPUB], -[Textile], [groff man] pages, [Emacs Org-Mode], [AsciiDoc], and [Slidy], -[Slideous], [DZSlides], or [S5] HTML slide shows. It can also produce -[PDF] output on systems where LaTeX is installed. +[LaTeX], [MediaWiki markup], and [DocBook XML]; and it can write plain +text, [markdown], [reStructuredText], [XHTML], [HTML 5], [LaTeX] +(including [beamer] slide shows), [ConTeXt], [RTF], [DocBook XML], +[OpenDocument XML], [ODT], [Word docx], [GNU Texinfo], [MediaWiki +markup], [EPUB] (v2 or v3), [FictionBook2], [Textile], [groff man] pages, [Emacs +Org-Mode], [AsciiDoc], and [Slidy], [Slideous], [DZSlides], or [S5] HTML +slide shows. It can also produce [PDF] output on systems where LaTeX is +installed. Pandoc's enhanced version of markdown includes syntax for footnotes, -tables, flexible ordered lists, definition lists, delimited code blocks, +tables, flexible ordered lists, definition lists, fenced code blocks, superscript, subscript, strikeout, title blocks, automatic tables of contents, embedded LaTeX math, citations, and markdown inside HTML block elements. (These enhancements, described below under -[Pandoc's markdown](#pandocs-markdown), can be disabled using the `--strict` -option.) +[Pandoc's markdown](#pandocs-markdown), can be disabled using the +`markdown_strict` input or output format.) In contrast to most existing tools for converting markdown to HTML, which use regex substitutions, Pandoc has a modular design: it consists of a @@ -43,7 +44,7 @@ If no *input-file* is specified, input is read from *stdin*. Otherwise, the *input-files* are concatenated (with a blank line between each) and used as input. Output goes to *stdout* by default (though output to *stdout* is disabled for the `odt`, `docx`, -and `epub` output formats). For output to a file, use the +`epub`, and `epub3` output formats). For output to a file, use the `-o` option: pandoc -o output.html input.txt @@ -107,7 +108,7 @@ to PDF: Production of a PDF requires that a LaTeX engine be installed (see `--latex-engine`, below), and assumes that the following LaTeX packages are available: `amssymb`, `amsmath`, `ifxetex`, `ifluatex`, `listings` (if the -`--listings` option is used), `fancyvrb`, `enumerate`, `ctable`, `url`, +`--listings` option is used), `fancyvrb`, `longtable`, `url`, `graphicx`, `hyperref`, `ulem`, `babel` (if the `lang` variable is set), `fontspec` (if `xelatex` or `lualatex` is used as the LaTeX engine), `xltxtra` and `xunicode` (if `xelatex` is used). @@ -117,12 +118,13 @@ and `xunicode` (if `xelatex` is used). A user who wants a drop-in replacement for `Markdown.pl` may create a symbolic link to the `pandoc` executable called `hsmarkdown`. When -invoked under the name `hsmarkdown`, `pandoc` will behave as if the -`--strict` flag had been selected, and no command-line options will be -recognized. However, this approach does not work under Cygwin, due to -problems with its simulation of symbolic links. +invoked under the name `hsmarkdown`, `pandoc` will behave as if +invoked with `-f markdown_strict --email-obfuscation=references`, +and all command-line options will be treated as regular arguments. +However, this approach does not work under Cygwin, due to problems with +its simulation of symbolic links. -[Cygwin]: http://www.cygwin.com/ +[Cygwin]: http://www.cygwin.com/ [`iconv`]: http://www.gnu.org/software/libiconv/ [CTAN]: http://www.ctan.org "Comprehensive TeX Archive Network" [TeX Live]: http://www.tug.org/texlive/ @@ -136,36 +138,52 @@ General options `-f` *FORMAT*, `-r` *FORMAT*, `--from=`*FORMAT*, `--read=`*FORMAT* : Specify input format. *FORMAT* can be `native` (native Haskell), - `json` (JSON version of native AST), `markdown` (markdown), + `json` (JSON version of native AST), `markdown` (pandoc's + extended markdown), `markdown_strict` (original unextended markdown), + `markdown_phpextra` (PHP Markdown Extra extended markdown), + `markdown_github` (github extended markdown), `textile` (Textile), `rst` (reStructuredText), `html` (HTML), - `docbook` (DocBook XML), or `latex` (LaTeX). If `+lhs` is - appended to `markdown`, `rst`, `latex`, the input will be - treated as literate Haskell source: see [Literate Haskell - support](#literate-haskell-support), below. + `docbook` (DocBook XML), `mediawiki` (MediaWiki markup), + or `latex` (LaTeX). If `+lhs` is appended to `markdown`, `rst`, + `latex`, the input will be treated as literate Haskell source: + see [Literate Haskell support](#literate-haskell-support), below. + Markdown syntax extensions can be individually enabled or disabled + by appending `+EXTENSION` or `-EXTENSION` to the format name. + So, for example, `markdown_strict+footnotes+definition_lists` + is strict markdown with footnotes and definition lists enabled, + and `markdown-pipe_tables+hard_line_breaks` is pandoc's markdown + without pipe tables and with hard line breaks. See [Pandoc's + markdown](#pandocs-markdown), below, for a list of extensions and + their names. `-t` *FORMAT*, `-w` *FORMAT*, `--to=`*FORMAT*, `--write=`*FORMAT* : Specify output format. *FORMAT* can be `native` (native Haskell), `json` (JSON version of native AST), `plain` (plain text), - `markdown` (markdown), `rst` (reStructuredText), `html` (XHTML 1), - `html5` (HTML 5), `latex` (LaTeX), `beamer` (LaTeX beamer slide show), + `markdown` (pandoc's extended markdown), `markdown_strict` (original + unextended markdown), `markdown_phpextra` (PHP Markdown extra + extended markdown), `markdown_github` (github extended markdown), + `rst` (reStructuredText), `html` (XHTML + 1), `html5` (HTML 5), `latex` (LaTeX), `beamer` (LaTeX beamer slide show), `context` (ConTeXt), `man` (groff man), `mediawiki` (MediaWiki markup), `textile` (Textile), `org` (Emacs Org-Mode), `texinfo` (GNU Texinfo), `docbook` (DocBook XML), `opendocument` (OpenDocument XML), `odt` - (OpenOffice text document), `docx` (Word docx), `epub` (EPUB book), - `asciidoc` (AsciiDoc), `slidy` (Slidy HTML and javascript slide show), - `slideous` (Slideous HTML and javascript slide show), - `dzslides` (HTML5 + javascript slide show), `s5` (S5 HTML and javascript - slide show), or `rtf` (rich text format). Note that `odt` and `epub` - output will not be directed to *stdout*; an output filename must - be specified using the `-o/--output` option. If `+lhs` is appended - to `markdown`, `rst`, `latex`, `beamer`, `html`, or `html5`, the output - will be rendered as literate Haskell source: see [Literate Haskell - support](#literate-haskell-support), below. + (OpenOffice text document), `docx` (Word docx), `epub` (EPUB book), `epub3` + (EPUB v3), `fb2` (FictionBook2 e-book), `asciidoc` (AsciiDoc), `slidy` + (Slidy HTML and javascript slide show), `slideous` (Slideous HTML and + javascript slide show), `dzslides` (HTML5 + javascript slide show), `s5` + (S5 HTML and javascript slide show), or `rtf` (rich text format). Note that + `odt`, `epub`, and `epub3` output will not be directed to *stdout*; an output + filename must be specified using the `-o/--output` option. If `+lhs` is + appended to `markdown`, `rst`, `latex`, `beamer`, `html`, or `html5`, the + output will be rendered as literate Haskell source: see [Literate Haskell + support](#literate-haskell-support), below. Markdown syntax extensions can + be individually enabled or disabled by appending `+EXTENSION` or + `-EXTENSION` to the format name, as described above under `-f`. `-o` *FILE*, `--output=`*FILE* : Write output to *FILE* instead of *stdout*. If *FILE* is `-`, output will go to *stdout*. (Exception: if the output - format is `odt`, `docx`, or `epub`, output to stdout is disabled.) + format is `odt`, `docx`, `epub`, or `epub3`, output to stdout is disabled.) `--data-dir=`*DIRECTORY* : Specify the user data directory to search for pandoc data files. @@ -191,12 +209,6 @@ General options Reader options -------------- -`--strict` -: Use strict markdown syntax, with no pandoc extensions or variants. - When the input format is HTML, this means that constructs that have no - equivalents in standard markdown (e.g. definition lists or strikeout - text) will be parsed as raw HTML. - `-R`, `--parse-raw` : Parse untranslatable HTML codes and LaTeX environments as raw HTML or LaTeX, instead of ignoring them. Affects only HTML and LaTeX @@ -213,9 +225,10 @@ Reader options to curly quotes, `---` to em-dashes, `--` to en-dashes, and `...` to ellipses. Nonbreaking spaces are inserted after certain abbreviations, such as "Mr." (Note: This option is significant only when - the input format is `markdown` or `textile`. It is selected automatically - when the input format is `textile` or the output format is `latex` or - `context`, unless `--no-tex-ligatures` is used.) + the input format is `markdown`, `markdown_strict`, or `textile`. It + is selected automatically when the input format is `textile` or the + output format is `latex` or `context`, unless `--no-tex-ligatures` + is used.) `--old-dashes` : Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: `-` before @@ -236,6 +249,8 @@ Reader options `-p`, `--preserve-tabs` : Preserve tabs instead of converting them to spaces (the default). + Note that this will only affect tabs in literal code spans and code + blocks; tabs in regular text will be treated as spaces. `--tab-stop=`*NUMBER* : Specify the number of spaces per tab (default is 4). @@ -246,7 +261,8 @@ General writer options `-s`, `--standalone` : Produce output with an appropriate header and footer (e.g. a standalone HTML, LaTeX, or RTF file, not a fragment). This option - is set automatically for `pdf`, `epub`, `docx`, and `odt` output. + is set automatically for `pdf`, `epub`, `epub3`, `fb2`, `docx`, and `odt` + output. `--template=`*FILE* : Use *FILE* as a custom template for the generated document. Implies @@ -284,6 +300,11 @@ General writer options one) in the output document. This option has no effect on `man`, `docbook`, `slidy`, `slideous`, or `s5` output. +`--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 + headers will be listed in the contents). Implies `--toc`. + `--no-highlight` : Disables syntax highlighting for code blocks and inlines, even when a language attribute is given. @@ -338,6 +359,9 @@ Options affecting specific writers : Produce HTML5 instead of HTML4. This option has no effect for writers other than `html`. (*Deprecated:* Use the `html5` output format instead.) +`--html-q-tags` +: Use `<q>` tags for quotes in HTML. + `--ascii` : Use only ascii characters in output. Currently supported only for HTML output (which uses numerical entities instead of @@ -399,13 +423,12 @@ Options affecting specific writers *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. - If `--strict` is specified, *references* is used regardless of the - presence of this option. `--id-prefix`=*STRING* : Specify a prefix to be added to all automatically generated identifiers - in HTML output. This is useful for preventing duplicate identifiers - when generating fragments to be included in other pages. + in HTML and DocBook output, and to footnote numbers in markdown 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 @@ -414,7 +437,8 @@ Options affecting specific writers `--standalone`. `-c` *URL*, `--css=`*URL* -: Link to a CSS style sheet. +: Link to a CSS style sheet. This option can be be used repeatedly to + include multiple files. They will be included in the order specified. `--reference-odt=`*FILE* : Use the specified file as a style reference in producing an ODT. @@ -434,7 +458,12 @@ Options affecting specific writers 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. + used. The following styles are used by pandoc: [paragraph] + Normal, Title, Authors, Date, Heading 1, Heading 2, Heading 3, + Heading 4, Heading 5, Block Quote, Definition Term, Definition, + Body Text, Table Caption, Image Caption; [character] Default + Paragraph Font, Body Text Char, Verbatim Char, Footnote Reference, + Hyperlink. `--epub-stylesheet=`*FILE* : Use the specified CSS file to style the EPUB. If no stylesheet @@ -495,19 +524,29 @@ Options affecting specific writers } body { font-family: "DejaVuSans"; } +`--epub-chapter-level=`*NUMBER* +: Specify the header level at which to split the EPUB into separate + "chapter" files. The default is to split into chapters at level 1 + headers. 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 headers, one might want to use a chapter + level of 2 or 3. + `--latex-engine=`*pdflatex|lualatex|xelatex* : Use the specified LaTeX engine when producing PDF output. The default is `pdflatex`. If the engine is not in your PATH, the full path of the engine may be specified here. -Citations ---------- +Citation rendering +------------------ `--bibliography=`*FILE* : Specify bibliography database to be used in resolving citations. The database type will be determined from the extension of *FILE*, which may be `.mods` (MODS format), - `.bib` (BibTeX/BibLaTeX format), + `.bib` (BibLaTeX format, which will normally work for BibTeX + files as well), `.bibtex` (BibTeX format), `.ris` (RIS format), `.enl` (EndNote format), `.xml` (EndNote XML format), `.wos` (ISI format), `.medline` (MEDLINE format), `.copac` (Copac format), @@ -587,7 +626,7 @@ Math rendering in HTML `--gladtex` : Enclose TeX math in `<eq>` tags in HTML output. These can then be processed by [gladTeX] to produce links to images of the typeset - formulas. + formulas. `--mimetex`[=*URL*] : Render TeX math using the [mimeTeX] CGI script. If *URL* is not @@ -624,8 +663,8 @@ Options for wrapper scripts [LaTeXMathML]: http://math.etsu.edu/LaTeXMathML/ [jsMath]: http://www.math.union.edu/~dpvc/jsmath/ [MathJax]: http://www.mathjax.org/ -[gladTeX]: http://www.math.uio.no/~martingu/gladtex/index.html -[mimeTeX]: http://www.forkosh.com/mimetex.html +[gladTeX]: http://ans.hsh.no/home/mgg/gladtex/ +[mimeTeX]: http://www.forkosh.com/mimetex.html [CSL]: http://CitationStyles.org Templates @@ -643,9 +682,7 @@ 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 `pdf` output, -customize the `default.latex` template. For `epub` output, customize -the `epub-page.html`, `epub-coverimage.html`, and `epub-titlepage.html` -templates. +customize the `default.latex` template. Templates may contain *variables*. Variable names are sequences of alphanumerics, `-`, and `_`, starting with a letter. A variable name @@ -754,8 +791,12 @@ 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 standard markdown. Except where noted, these -differences can be suppressed by specifying the `--strict` command-line -option. +differences can be suppressed by using the `markdown_strict` format instead +of `markdown`. An extensions can be enabled by adding `+EXTENSION` +to the format name and disabled by adding `-EXTENSION`. For example, +`markdown_strict+footnotes` is strict markdown with footnotes +enabled, while `markdown-footnotes-pipe_tables` is pandoc's +markdown without footnotes or pipe tables. Philosophy ---------- @@ -784,8 +825,11 @@ Paragraphs A paragraph is one or more lines of text followed by one or more blank line. 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, -or type a backslash followed by a newline. +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. Headers ------- @@ -821,6 +865,8 @@ As with setext-style headers, the header text can contain formatting: # A level-one header with a [link](/url) and *emphasis* +**Extension: `blank_before_header`** + Standard markdown syntax does not require a blank line before a header. 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 @@ -833,10 +879,31 @@ wrapping). Consider, for example: ### Header identifiers in HTML, LaTeX, and ConTeXt ### -*Pandoc extension*. +**Extension: `header_attributes`** -Each header element in pandoc's HTML and ConTeXt output is given a -unique identifier. This identifier is based on the text of the header. +Headers can be assigned attributes using this syntax at the end +of the line containing the header text: + + {#identifier .class .class key=value key=value} + +Although this syntax allows assignment of classes and key/value attributes, +only identifiers currently have any affect in the writers (and only in some +writers: HTML, LaTeX, ConTeXt, Textile, AsciiDoc). Thus, for example, +the following headers will all be assigned the identifier `foo`: + + # My header {#foo} + + ## My header ## {#foo} + + My other header {#foo} + --------------- + +(This syntax is compatible with [PHP Markdown Extra].) + +**Extension: `auto_identifiers`** + +A header without an explicitly specified identifier will be +automatically assigned a unique identifier based on the header text. To derive the identifier from the header text, - Remove all formatting, links, etc. @@ -881,6 +948,33 @@ and the identifier will be attached to the enclosing `<div>` sections to be manipulated using javascript or treated differently in CSS. +**Extension: `implicit_header_references`** + +Pandoc behaves as if reference links have been defined for each header. +So, instead of + + [header identifiers](#header-identifiers-in-html) + +you can simply write + + [header identifiers] + +or + + [header identifiers][] + +or + + [the section on header identifiers][header identifiers] + +If there are multiple headers 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. + +Unlike regular reference links, these references are case-sensitive. + +Note: if you have defined an explicit identifier for a header, +then implicit references to it will not work. Block quotations ---------------- @@ -913,12 +1007,14 @@ other block quotes. That is, block quotes can be nested: > > > A block quote within a block quote. +**Extension: `blank_line_before_blockquote`** + Standard 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 `--strict` is used, the following does not produce -a nested block quote in pandoc: +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. >> Nested. @@ -943,12 +1039,12 @@ of the verbatim text, and is removed in the output. Note: blank lines in the verbatim text need not begin with four spaces. -### Delimited code blocks ### +### Fenced code blocks ### -*Pandoc extension*. +**Extension: `fenced_code_blocks`** In addition to standard indented code blocks, Pandoc supports -*delimited* code blocks. These begin with a row of three or more +*fenced* code blocks. These begin with a row of three or more tildes (`~`) or backticks (`` ` ``) and end with a row of tildes or backticks that must be at least as long as the starting row. Everything between these lines is treated as code. No indentation is necessary: @@ -959,7 +1055,7 @@ between these lines is treated as code. No indentation is necessary: } ~~~~~~~ -Like regular code blocks, delimited code blocks must be separated +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 @@ -1010,6 +1106,35 @@ This is equivalent to: To prevent all highlighting, use the `--no-highlight` flag. To set the highlighting style, use `--highlight-style`. +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 + +This syntax is borrowed from [reStructuredText]. + Lists ----- @@ -1126,7 +1251,7 @@ and this one: 7. two 1. three -*Pandoc extension*. +**Extension: `fancy_lists`** Unlike standard markdown, Pandoc allows ordered list items to be marked with uppercase and lowercase letters and roman numerals, in addition to @@ -1151,6 +1276,8 @@ capital letter with a period, by at least two spaces.[^2] (C\) 2007 Joe Smith +**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 @@ -1181,7 +1308,7 @@ If default list markers are desired, use `#.`: ### Definition lists ### -*Pandoc extension*. +**Extension: `definition_lists`** Pandoc supports definition lists, using a syntax inspired by [PHP Markdown Extra] and [reStructuredText]:[^3] @@ -1226,7 +1353,7 @@ definition and the next term: ### Numbered example lists ### -*Pandoc extension*. +**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', @@ -1255,7 +1382,7 @@ or hyphens. ### Compact and loose lists ### Pandoc behaves differently from `Markdown.pl` on some "edge -cases" involving lists. Consider this source: +cases" involving lists. Consider this source: + First + Second: @@ -1272,7 +1399,7 @@ around "Third". Pandoc follows a simple rule: if the text is followed by a blank line, it is treated as a paragraph. Since "Second" is followed by a list, and not a blank line, it isn't treated as a paragraph. The fact that the list is followed by a blank line is irrelevant. (Note: -Pandoc works this way even when the `--strict` option is specified. This +Pandoc works this way even when the `markdown_strict` format is specified. This behavior is consistent with the official markdown syntax description, even though it is different from that of `Markdown.pl`.) @@ -1328,12 +1455,16 @@ A line containing a row of three or more `*`, `-`, or `_` characters Tables ------ -*Pandoc extension*. +**Extension: `simple_tables`, `multiline_tables`, `grid_tables`, +`pipe_tables`, `table_captions`** -Three kinds of tables may be used. All three kinds presuppose the use of -a fixed-width font, such as Courier. +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. -**Simple tables** look like this: +### Simple tables + +Simple tables look like this: Right Left Center Default ------- ------ ---------- ------- @@ -1349,7 +1480,7 @@ 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 + - 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. @@ -1378,7 +1509,9 @@ When headers are 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. -**Multiline tables** allow headers and table rows to span multiple lines +### Multiline tables + +Multiline tables allow headers 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: @@ -1418,7 +1551,7 @@ Headers may be omitted in multiline tables as well as simple tables: Second row 5.0 Here's another one. Note the blank line between rows. - ------------------------------------------------------------- + ----------- ------- --------------- ------------------------- : Here's a multiline table without headers. @@ -1426,7 +1559,9 @@ 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. -**Grid tables** look like this: +### Grid tables + +Grid tables look like this: : Sample grid table. @@ -1448,11 +1583,56 @@ columns or rows. Grid tables can be created easily using [Emacs table mode]. [Emacs table mode]: http://table.sourceforge.net/ +### 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 simple table syntax. + +The syntax is [the same as in PHP markdown extra]. The beginning and +ending pipe characters are optional, but pipes are required between all +columns. The colons indicate column alignment as shown. The header +can be omitted, but the horizontal line must still be included, as +it defines column alignments. + +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. + + [the same as in PHP markdown extra]: + http://michelf.ca/projects/php-markdown/extra/#table + +Note: Pandoc also recognizes pipe tables of the following +form, as can 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. Title block ----------- -*Pandoc extension*. +**Extension: `pandoc_title_block`** If the file begins with a title block @@ -1532,6 +1712,8 @@ will also have "Version 4.0" in the header. 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 @@ -1551,8 +1733,8 @@ which allows only the following characters to be backslash-escaped: \`*_{}[]()>#+-.! -(However, if the `--strict` option is supplied, the standard -markdown rule will be used.) +(However, if the `markdown_strict` format is used, the standard markdown rule +will be used.) A backslash-escaped space is parsed as a nonbreaking space. It will appear in TeX output as `~` and in HTML and XML as `\ ` or @@ -1569,7 +1751,7 @@ Backslash escapes do not work in verbatim contexts. Smart punctuation ----------------- -*Pandoc extension*. +**Extension** If the `--smart` option is specified, pandoc will produce typographically correct output, converting straight quotes to curly quotes, `---` to @@ -1598,6 +1780,8 @@ 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 @@ -1608,7 +1792,7 @@ just part of a word, use `*`: ### Strikeout ### -*Pandoc extension*. +**Extension: `strikeout`** To strikeout a section of text with a horizontal line, begin and end it with `~~`. Thus, for example, @@ -1618,7 +1802,7 @@ with `~~`. Thus, for example, ### Superscripts and subscripts ### -*Pandoc extension*. +**Extension: `superscript`, `subscript`** Superscripts may be written by surrounding the superscripted text by `^` characters; subscripts may be written by surrounding the subscripted @@ -1656,15 +1840,17 @@ 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 -[delimited code blocks](#delimited-code-blocks): +[fenced code blocks](#fenced-code-blocks): `<$>`{.haskell} Math ---- -*Pandoc extension*. +**Extension: `tex_math_dollars`** Anything between two `$` characters will be treated as TeX math. The opening `$` must have a character immediately to its right, while the @@ -1710,7 +1896,12 @@ Docbook Docx ~ It will be rendered using OMML math markup. -HTML, Slidy, Slideous, DZSlides, S5, EPUB +FictionBook2 + ~ If the `--webtex` option is used, formulas are rendered as images + using Google Charts 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: @@ -1720,11 +1911,11 @@ HTML, Slidy, Slideous, DZSlides, S5, EPUB styled differently from the surrounding text if needed. 2. If the `--latexmathml` option is used, TeX math will be displayed - between $ or $$ characters and put in `<span>` tags with class `LaTeX`. + between `$` or `$$` characters and put in `<span>` tags with class `LaTeX`. The [LaTeXMathML] script will be used to render it as formulas. (This trick does not work in all browsers, but it works in Firefox. In browsers that do not support LaTeXMathML, TeX math will appear - verbatim between $ characters.) + verbatim between `$` characters.) 3. If the `--jsmath` option is used, TeX math will be put inside `<span>` tags (for inline math) or `<div>` tags (for display math) @@ -1753,19 +1944,27 @@ HTML, Slidy, Slideous, DZSlides, S5, EPUB with the URL provided. If no URL is specified, the Google Chart API will be used (`http://chart.apis.google.com/chart?cht=tx&chl=`). + 7. If the `--mathjax` option is used, TeX math will be displayed + between `\(...\)` (for inline math) or `\[...\]` (for display + math) and put in `<span>` tags with class `math`. + The [MathJax] script will be used to render it as formulas. 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). +literally). (Techncially 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, and Textile output, and suppressed in other formats. +DZSlides, EPUB, Markdown, and Textile output, and suppressed in other +formats. -*Pandoc extension*. +**Extension: `markdown_in_html_blocks`** Standard markdown allows you to include HTML "blocks": blocks of HTML between balanced tags that are separated from the surrounding text @@ -1773,8 +1972,8 @@ 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 `--strict` is specified; but by default, -pandoc interprets material between HTML block tags as markdown. +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 <table> @@ -1806,7 +2005,7 @@ from being interpreted as markdown. Raw TeX ------- -*Pandoc extension*. +**Extension: `raw_tex`** In addition to raw HTML, pandoc allows raw LaTeX, TeX, and ConTeXt to be included in a document. Inline TeX commands will be preserved and passed @@ -1820,7 +2019,7 @@ Note that in LaTeX environments, like \begin{tabular}{|l|l|}\hline Age & Frequency \\ \hline 18--25 & 15 \\ - 26--35 & 33 \\ + 26--35 & 33 \\ 36--45 & 22 \\ \hline \end{tabular} @@ -1830,7 +2029,10 @@ LaTeX, not as markdown. Inline LaTeX is ignored in output formats other than Markdown, LaTeX, and ConTeXt. -### Macros ### +LaTeX macros +------------ + +**Extension: `latex_macros`** For output formats other than LaTeX, pandoc will parse LaTeX `\newcommand` and `\renewcommand` definitions and apply the resulting macros to all LaTeX @@ -1858,7 +2060,6 @@ will become a link: <http://google.com> <sam@green.eggs.ham> - ### Inline links ### An inline link consists of the link text in square brackets, @@ -1880,7 +2081,6 @@ before or after the link). The link consists of link text in square brackets, followed by a label in square brackets. (There can be space between the two.) The link definition -must begin at the left margin or indented no more than three spaces. It consists of the bracketed label, followed by a colon and a space, followed by the URL, and optionally (after a space) a link title either in quotes or in parentheses. @@ -1914,6 +2114,16 @@ empty, or omitted entirely: [my website]: http://foo.bar.baz +Note: In `Markdown.pl` and most other markdown implementations, +reference link definitions cannot occur in nested constructions +such as list items or block quotes. Pandoc lifts this arbitrary +seeming restriction. So the following is fine in pandoc, though +not in most other implementations: + + > My block [quote]. + > + > [quote]: /foo + ### Internal links To link to another section of the same document, use the automatically @@ -1946,7 +2156,7 @@ The link text will be used as the image's alt text: ### Pictures with captions ### -*Pandoc extension*. +**Extension: `implicit_figures`** An image occurring by itself in a paragraph will be rendered as a figure with a caption.[^5] (In LaTeX, a figure environment will be @@ -1964,13 +2174,13 @@ If you just want a regular inline image, just make sure it is not the only thing in the paragraph. One way to do this is to insert a nonbreaking space after the image: - ![This image won't be a figure](/url/of/image.png)\ + ![This image won't be a figure](/url/of/image.png)\ Footnotes --------- -*Pandoc extension*. +**Extension: `footnotes`** Pandoc's markdown allows footnotes, using the following syntax: @@ -1980,7 +2190,7 @@ Pandoc's markdown allows footnotes, using the following syntax: [^longnote]: Here's one with multiple blocks. - Subsequent paragraphs are indented to show that they + Subsequent paragraphs are indented to show that they belong to the previous footnote. { some.code } @@ -2001,6 +2211,8 @@ The footnotes themselves need not be placed at the end of the document. They may appear anywhere except inside other block elements (lists, block quotes, tables, etc.). +**Extension: `inline_notes`** + Inline footnotes are also allowed (though, unlike regular notes, they cannot contain multiple paragraphs). The syntax is as follows: @@ -2014,7 +2226,7 @@ Inline and regular footnotes may be mixed freely. Citations --------- -*Pandoc extension*. +**Extension: `citations`** Pandoc can automatically generate citations and a bibliography in a number of styles (using Andrea Rossato's `hs-citeproc`). In order to use this feature, @@ -2023,7 +2235,8 @@ you will need a bibliographic database in one of the following formats: Format File extension ------------ -------------- MODS .mods - BibTeX/BibLaTeX .bib + BibLaTeX .bib + BibTeX .bibtex RIS .ris EndNote .enl EndNote XML .xml @@ -2032,6 +2245,9 @@ you will need a bibliographic database in one of the following formats: Copac .copac JSON citeproc .json +Note that `.bib` can generally be used with both BibTeX and BibLaTeX +files, but you can use `.bibtex` to force BibTeX. + You will need to specify the bibliography file using the `--bibliography` command-line option (which may be repeated if you have several bibliographies). @@ -2078,6 +2294,72 @@ document with an appropriate header: The bibliography will be inserted after this header. +Non-pandoc extensions +===================== + +The following markdown syntax extensions are not enabled by default +in pandoc, but may be enabled by adding `+EXTENSION` to the format +name, where `EXTENSION` is the name of the extension. Thus, for +example, `markdown+hard_line_breaks` is markdown with hard line breaks. + +**Extension: `hard_line_breaks`**\ +Causes all newlines within a paragraph to be interpreted as hard line +breaks instead of spaces. + +**Extension: `tex_math_single_backslash`**\ +Causes anything between `\(` and `\)` to be interpreted as inline +TeX math, and anything between `\[` and `\]` to be interpreted +as display TeX math. Note: a drawback of this extension is that +it precludes escaping `(` and `[`. + +**Extension: `tex_math_double_backslash`**\ +Causes anything between `\\(` and `\\)` to be interpreted as inline +TeX math, and anything between `\\[` and `\\]` to be interpreted +as display TeX math. + +**Extension: `markdown_attribute`**\ +By default, pandoc interprets material inside block-level tags as markdown. +This extension changes the behavior so that markdown is only parsed +inside block-level tags if the tags have the attribute `markdown=1`. + +**Extension: `mmd_title_block`**\ +Enables a [MultiMarkdown] style title block at the top of +the document, for example: + + Title: My title + Author: John Doe + Date: September 1, 2008 + Comment: This is a sample mmd title block, with + a field spanning multiple lines. + +See the MultiMarkdown documentation for details. Note that only title, +author, and date are recognized; other fields are simply ignored by +pandoc. If `pandoc_title_block` is enabled, it will take precedence over +`mmd_title_block`. + + [MultiMarkdown]: http://fletcherpenney.net/multimarkdown/ + +**Extension: `abbrevations`**\ +Parses PHP Markdown Extra abbreviation keys, like + + *[HTML]: Hyper Text Markup Language + +Note that the pandoc document model does not support +abbreviations, so if this extension is enabled, abbreviation keys are +simply skipped (as opposed to being parsed as paragraphs). + +**Extension: `autolink_bare_uris`**\ +Makes all absolute URIs into links, even when not surrounded by +pointy braces `<...>`. + +**Extension: `link_attributes`**\ +Parses multimarkdown style key-value attributes on link and image references. +Note that pandoc's internal document model provides nowhere to put +these, so they are presently just ignored. + +**Extension: `mmd_header_identifiers`**\ +Parses multimarkdown style header identifiers (in square brackets, +after the header but before any trailing `#`s in an ATX header). Producing slide shows with Pandoc ================================= @@ -2228,10 +2510,10 @@ using the `-V` option: Literate Haskell support ======================== -If you append `+lhs` to an appropriate input or output format (`markdown`, -`rst`, or `latex` for input or output; `beamer`, `html` or `html5` for -output only), pandoc will treat the document as literate Haskell source. -This means that +If you append `+lhs` (or `+literate_haskell`) to an appropriate input or output +format (`markdown`, `mardkown_strict`, `rst`, or `latex` for input or output; +`beamer`, `html` or `html5` for output only), 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}` @@ -2282,7 +2564,8 @@ Andrea Rossato, Eric Kow, infinity0x, Luke Plant, shreevatsa.public, Puneeth Chaganti, Paul Rivier, rodja.trappe, Bradley Kuhn, thsutton, Nathan Gass, Jonathan Daugherty, Jérémy Bobbio, Justin Bogner, qerub, Christopher Sawicki, Kelsey Hightower, Masayoshi Takahashi, Antoine -Latter, Ralf Stephan, Eric Seidel, B. Scott Michel, Gavin Beatty. +Latter, Ralf Stephan, Eric Seidel, B. Scott Michel, Gavin Beatty, +Sergey Astanin, Arlo O'Keeffe, Denis Laxalde, Brent Yorgey. [markdown]: http://daringfireball.net/projects/markdown/ [reStructuredText]: http://docutils.sourceforge.net/docs/ref/rst/introduction.html @@ -2294,10 +2577,10 @@ Latter, Ralf Stephan, Eric Seidel, B. Scott Michel, Gavin Beatty. [XHTML]: http://www.w3.org/TR/xhtml1/ [LaTeX]: http://www.latex-project.org/ [beamer]: http://www.tex.ac.uk/CTAN/macros/latex/contrib/beamer -[ConTeXt]: http://www.pragma-ade.nl/ +[ConTeXt]: http://www.pragma-ade.nl/ [RTF]: http://en.wikipedia.org/wiki/Rich_Text_Format [DocBook XML]: http://www.docbook.org/ -[OpenDocument XML]: http://opendocument.xml.org/ +[OpenDocument XML]: http://opendocument.xml.org/ [ODT]: http://en.wikipedia.org/wiki/OpenDocument [Textile]: http://redcloth.org/textile [MediaWiki markup]: http://www.mediawiki.org/wiki/Help:Formatting @@ -2312,3 +2595,4 @@ Latter, Ralf Stephan, Eric Seidel, B. Scott Michel, Gavin Beatty. [ISO 8601 format]: http://www.w3.org/TR/NOTE-datetime [Word docx]: http://www.microsoft.com/interop/openup/openxml/default.aspx [PDF]: http://www.adobe.com/pdf/ +[FictionBook2]: http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1 @@ -1,4 +1,5 @@ -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE CPP #-} + import Distribution.Simple import Distribution.Simple.Setup (copyDest, copyVerbosity, fromFlag, installVerbosity, BuildFlags(..)) @@ -33,10 +34,10 @@ main = do -- | Build man pages from markdown sources in man/ makeManPages :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO () -makeManPages _ flags _ _ = do +makeManPages _ flags _ lbi = do let verbosity = fromFlag $ buildVerbosity flags let args = ["--verbose" | verbosity /= silent] - rawSystem ("dist" </> "build" </> "make-pandoc-man-pages" </> "make-pandoc-man-pages") + rawSystem (buildDir lbi </> "make-pandoc-man-pages" </> "make-pandoc-man-pages") args >>= exitWith manpages :: [FilePath] @@ -51,3 +52,4 @@ installManpages :: PackageDescription -> LocalBuildInfo installManpages pkg lbi verbosity copy = installOrdinaryFiles verbosity (mandir (absoluteInstallDirs pkg lbi copy)) (zip (repeat manDir) manpages) + diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs new file mode 100644 index 000000000..2990bed87 --- /dev/null +++ b/benchmark/benchmark-pandoc.hs @@ -0,0 +1,46 @@ +import Text.Pandoc +import Text.Pandoc.Shared (normalize) +import Criterion.Main +import Criterion.Config +import Text.JSON.Generic +import System.Environment (getArgs) +import Data.Monoid + +readerBench :: Pandoc + -> (String, ReaderOptions -> String -> IO Pandoc) + -> Benchmark +readerBench doc (name, reader) = + let writer = case lookup name writers of + Just (PureStringWriter w) -> w + _ -> error $ "Could not find writer for " ++ name + inp = writer def{ writerWrapText = True } doc + -- we compute the length to force full evaluation + getLength (Pandoc (Meta a b c) d) = + length a + length b + length c + length d + in bench (name ++ " reader") $ whnfIO $ getLength `fmap` + (reader def{ readerSmart = True }) inp + +writerBench :: Pandoc + -> (String, WriterOptions -> Pandoc -> String) + -> Benchmark +writerBench doc (name, writer) = bench (name ++ " writer") $ nf + (writer def{ writerWrapText = True }) doc + +normalizeBench :: Pandoc -> [Benchmark] +normalizeBench doc = [ bench "normalize - with" $ nf (encodeJSON . normalize) doc + , bench "normalize - without" $ nf encodeJSON doc + ] + +main :: IO () +main = do + args <- getArgs + (conf,_) <- parseArgs defaultConfig{ cfgSamples = Last $ Just 20 } defaultOptions args + inp <- readFile "README" + inp2 <- readFile "tests/testsuite.txt" + let opts = def{ readerSmart = True } + let doc = readMarkdown opts $ inp ++ unlines (drop 3 $ lines inp2) + let readerBs = map (readerBench doc) readers + let writers' = [(n,w) | (n, PureStringWriter w) <- writers] + defaultMainWith conf (return ()) $ + map (writerBench doc) writers' ++ readerBs ++ normalizeBench doc + @@ -1,29 +1,732 @@ -pandoc (1.9.4.5) +pandoc (1.10.0.3) - * Raised version bounds on network, base64-bytestring, json, - and template-haskell. + * Added further missing fb2 tests to cabal file. -pandoc (1.9.4.4) +pandoc (1.10.0.2) - * Removed `tests` flag and made test suite into a proper cabal - test suite, which can now be enabled using `--enable-tests` - and run with `cabal test`. + * Added fb2 tests to cabal file's extra-source-files. - * Moved man page creation out of `Setup.hs` and into an - executable built by Cabal, but never installed. This - allows dependencies to be specified, and solves a problem - with 1.9.4.3, which could only be installed if `data-default` - had already been installed. +pandoc (1.10.0.1) - * Updated `lhs-latex.tex` test for latest highlighting-kate - representation of backticks. + * Bump version bounds on test-framework packages. -pandoc (1.9.4.3) +pandoc (1.10) - * Removed `-threaded` from default compile flags. + [new features] + + * New input formats: `mediawiki` (MediaWiki markup). + + * New output formats: `epub3` (EPUB v3 with MathML), + `fb2` (FictionBook2 ebooks). + + * New `--toc-depth` option, specifying how many levels of + headers to include in a table of contents. + + * New `--epub-chapter-level` option, specifying the header + level at which to divide EPUBs into separate files. + Note that this normally affects only performance, not the + visual presentation of the EPUB in a reader. + + * Removed the `--strict` option. Instead of using `--strict`, + one can now use the format name `markdown_strict` for either input + or output. This gives more fine-grained control that `--strict` + did, allowing one to convert from pandoc's markdown to strict + markdown or vice versa. + + * It is now possible to enable or disable specific syntax + extensions by appending them (with `+` or `-`) to the writer + or reader name. For example, + + pandoc -f markdown-footnotes+hard_line_breaks + + disables footnotes and enables treating newlines as hard + line breaks. The literate Haskell extensions are now implemented + this way as well, using either `+lhs` or `+literate_haskell`. + For a list of extension names, see the README under + "Pandoc's Markdown." + + * The following aliases have been introduced for specific + combinations of markdown extensions: `markdown_phpextra`, + `markdown_github`, `markdown_mmd`, `markdown_strict`. These aliases + work just like regular reader and writer names, and can be modified + with extension modifiers as described above. (Note that conversion + from one markdown dialect to another does not work perfectly, + because there are differences in markdown parsers besides + just the extensions, and because pandoc's internal document model is + not rich enough to capture all of the extensions.) + + * New `--html-q-tags` option. The previous default was to use `<q>` + tags for smart quotes in HTML5. But `<q>` tags are also valid HTML4. + Moreover, they are not a robust way of typesetting quotes, since + some user agents don't support them, and some CSS resets (e.g. + bootstrap) prevent pandoc's quotes CSS from working properly. + We now just insert literal quote characters by default in both + `html` and `html5` output, but this option is provided for + those who still want `<q>` tags. + + * The markdown reader now prints warnings (to stderr) about + duplicate link and note references. Closes #375. + + * Markdown syntax extensions: + + + Added pipe tables. Thanks to François Gannaz for the initial patch. + These conform to PHP Markdown Extra's pipe table syntax. A subset + of org-mode table syntax is also supported, which means that you can + use org-mode's nice table editor to create tables. + + + Added support for RST-style line blocks. These are + useful for verse and addresses. + + + Attributes can now be specified for headers, using the same + syntax as in code blocks. (However, currently only the + identifier has any effect in most writers.) For example, + + # My header {#foo} + + See [the header above](#foo). + + + Pandoc will now act as if link references have been defined + for all headers without explicit identifiers. + So, you can do this: + + # My header + + Link to [My header]. + Another link to [it][My header]. + + Closes #691. + + * LaTeX reader: + + + Command macros now work everywhere, including non-math. + Environment macros still not supported. + + `\input` now works, as well as `\include`. TEXINPUTS is used. + Pandoc looks recursively into included files for more included files. + + [behavior changes] + + * The Markdown reader no longer puts the text of autolinks in a + `Code` inline. This means that autolinks will no longer appear + in a monospace font. + + * The character `/` can now appear in markdown citation keys. + + * HTML blocks in strict_markdown are no longer required to begin + at the left margin. Technically this is required, according to + the markdown syntax document, but `Markdown.pl` and other markdown + processors are more liberal. + + * The `-V` option has been changed so that if there are duplicate + variables, those specified later on the command line take precedence. + + * Tight lists now work in LaTeX and ConTeXt output. + + * The LaTeX writer no longer relien on the `enumerate` package. + Instead, it uses standard LaTeX commands to change the list numbering + style. + + * The LaTeX writer now uses `longtable` instead of `ctable`. This allows + tables to be split over page boundaries. + + * The RST writer now uses a line block to render paragraphs containing + linebreaks (which previously weren't supported at all). + + * The markdown writer now applies the `--id-prefix` to footnote IDs. + Closes #614. + + * The plain writer no longer uses backslash-escaped line breaks + (which are not very "plain"). + + * `Text.Pandoc.UTF8`: Better error message for invalid UTF8. + Read bytestring and use `Text`'s decodeUtf8 instead of using + `System.IO.hGetContents`. This way you get a message saying + "invalid UTF-8 stream" instead of "invalid byte sequence." + You are also told which byte caused the problem. + + * Docx, ODT, and EPUB writers now download images specified by a URL + instead of skipping them or raising an error. + + * EPUB writer: + + + The default CSS now left-aligns headers by default, instead of + centering. This is more consistent with the rest of the writers. + + A proper multi-level table of contents is now used in `toc.ncx`. + There is no longer a subsidiary table of contents at the beginning + of each chapter. + + Code highlighting now works by default. + + Section divs are used by default for better semantic markup. + + The title is used instead of "Title Page" in the table of contents. + Otherwise we have a hard-coded English string, which looks + strange in ebooks written in other languages. Closes #572. + + * HTML writer: + + + Put mathjax in span with class "math". Closes #562. + + Put citations in a span with class "citation." In HTML5, also include + a `data-cite` attribute with a space-separated list of citation + keys. + + * `Text.Pandoc.UTF8`: use universalNewlineMode in reading. + This treats both `\r\n` and `\n` as `\n` on input, no matter + what platform we're running on. + + * Citation processing is now done in the Markdown and LaTeX + readers, not in `pandoc.hs`. This makes it easier for library users + to use citations. + + [template changes] + + * HTML: Added css to template to preserve spaces in `<code>` tags. + Thanks to Dirk Laurie. + + * Beamer: Remove English-centric strings in section pages. + Section pages used to have "Section" and a number as well as the + section title. Now they just have the title. Similarly for part + and subsection. Closes #566. + + * LaTeX, ConTeXt: Added papersize variable. + + * LaTeX, Beamer templates: Use longtable instead of ctable. + + * LaTeX, Beamer templates: Don't require 'float' package for tables. + We don't actually seem to use the '[H]' option. + + * LaTeX: Use `upquote` package if it is available. This fixes + straight quotes in verbatim environments. + + * Markdown, plain: Fixed titleblock so it is just a single string. + Previously separate title, author, and date variables were used, + but this didn't allow different kinds of title blocks. + + * EPUB: + + + Rationalized templates. Previously there were three different + templates involved in epub production. There is now just one + template, `default.epub` or `default.epub3`. It can now be + overridden using `--template`, just like other templates. + The titlepage is now folded into the default template. + A `titlepage` variable selects it. + + UTF-8, lang tag, meta tags, title element. + + * Added scale-to-width feature to beamer template + + [API changes] + + * `Text.Pandoc.Definition`: Added `Attr` field to `Header`. + Previously header identifers were autogenerated by the writers. + Now they are added in the readers (either automatically or explicitly). + + * `Text.Pandoc.Builder`: + + + `Inlines` and `Blocks` are now synonyms for `Many Inline` and + `Many Block`. `Many` is a newtype wrapper around `Seq`, with + custom Monoid instances for `Many Inline` and `Many Block. This + allows `Many` to be made an instance of `Foldable` and `Traversable`. + + The old `Listable` class has been removed. + + The module now exports `isNull`, `toList`, `fromList`. + + The old `Read` and `Show` instances have been removed; derived + instances are now used. + + Added `headerWith`. + + * The readers now take a `ReaderOptions` rather than a `ParserState` + as a parameter. Indeed, not all parsers use the `ParserState` type; + some have a custom state. The motivation for this change was to separate + user-specifiable options from the accounting functions of parser state. + + * New module `Text.Pandoc.Options`. This includes the `WriterOptions` + formerly in `Text.Pandoc.Shared`, and its associated + data types. It also includes a new type `ReaderOptions`, which + contains many options formerly in `ParserState`, and its associated + data types: + + + `ParserState.stateParseRaw` -> `ReaderOptions.readerParseRaw`. + + `ParserState.stateColumns` -> `ReaderOptions.readerColumns`. + + `ParserState.stateTabStop` -> `ReaderOptions.readerTabStop`. + + `ParserState.stateOldDashes` -> `ReaderOptions.readerOldDashes`. + + `ParserState.stateLiterateHaskell` -> `ReaderOptions.readerLiterateHaskell`. + + `ParserState.stateCitations` -> `ReaderOptions.readerReferences`. + + `ParserState.stateApplyMacros` -> `ReaderOptions.readerApplyMacros`. + + `ParserState.stateIndentedCodeClasses` -> + `ReaderOptions.readerIndentedCodeClasses`. + + Added `ReaderOptions.readerCitationStyle`. + + * `WriterOptions` now includes `writerEpubVersion`, `writerEpubChapterLevel`, + `writerEpubStylesheet`, `writerEpubFonts`, `writerReferenceODT`, + `writerReferenceDocx`, and `writerTOCDepth`. `writerEPUBMetadata` has + been renamed `writerEpubMetadata` for consistency. + + * Changed signatures of `writeODT`, `writeDocx`, `writeEPUB`, since they no + longer stylesheet, fonts, reference files as separate parameters. + + * Removed `writerLiterateHaskell` from `WriterOptions`, and + `readerLiterateHaskell` from `ReaderOptions`. LHS is now handled + by an extension (`Ext_literate_haskell`). + + * Removed deprecated `writerXeTeX`. + + * Removed `writerStrict` from `WriterOptions`. Added `writerExtensions`. + Strict is now handled through extensions. + + * `Text.Pandoc.Options` exports `pandocExtensions`, `strictExtensions`, + `phpMarkdownExtraExtensions`, `githubMarkdownExtensions`, + and `multimarkdownExtensions`, as well as the `Extensions` type. + + * New `Text.Pandoc.Readers.MediaWiki` module, exporting + `readMediaWiki`. + + * New `Text.Pandoc.Writers.FB2` module, exporting `writeFB2` + (thanks to Sergey Astanin). + + * `Text.Pandoc`: + + + Added `getReader`, `getWriter` to `Text.Pandoc`. + + `writers` is now an association list `(String, Writer)`. + A `Writer` can be a `PureStringWriter`, an `IOStringWriter`, or + an `IOByteStringWriter`. ALL writers are now in the 'writers' + list, including the binary writers and FB2 writer. This allows + code in `pandoc.hs` to be simplified. + + Changed type of `readers`, so all readers are in IO. + Users who want pure readers can still get them form the reader + modules; this just affects the function `getReader` that looks up + a reader based on the format name. The point of this change is to + make it possible to print warnings from the parser. + + * `Text.Pandoc.Parsing`: + + + `Text.Parsec` now exports all Parsec functions used in pandoc code. + No other module directly imports Parsec. This will make it easier + to change the parsing backend in the future, if we want to. + + `Text.Parsec` is used instead of `Text.ParserCombinators.Parsec`. + + Export the type synonym `Parser`. + + Export `widthsFromIndices`, `NoteTable'`, `KeyTable'`, `Key'`, `toKey'`, + `withQuoteContext`, `singleQuoteStart`, `singleQuoteEnd`, + `doubleQuoteStart`, `doubleQuoteEnd`, `ellipses`, `apostrophe`, + `dash`, `nested`, `F(..)`, `askF`, `asksF`, `runF`, `lineBlockLines`. + + `ParserState` is no longer an instance of `Show`. + + Added `stateSubstitutions` and `stateWarnings` to `ParserState`. + + Generalized type of `withQuoteContext`. + + Added `guardEnabled`, `guardDisabled`, `getOption`. + + Removed `failIfStrict`. + + `lookupKeySrc` and `fromKey` are no longer exported. + + * `Data.Default` instances are now provided for `ReaderOptions`, + `WriterOptions`, and `ParserState`. `Text.Pandoc` re-exports `def`. + Now you can use `def` (which is re-exported by `Text.Pandoc`) instead + of `defaultWriterOptions` (which is still defined). Closes #546. + + * `Text.Pandoc.Shared`: + + + Added `safeRead`. + + Renamed `removedLeadingTrailingSpace` to `trim`, + `removeLeadingSpace` to `triml`, and `removeTrailingSpace` to `trimr`. + + Count `\r` as space in `trim` functions. + + Moved `renderTags'` from HTML reader and `Text.Pandoc.SelfContained` + to `Shared`. + + Removed `failUnlessLHS`. + + Export `compactify'`, formerly in Markdown reader. + + Export `isTightList`. + + Do not export `findDataFile`. + + `readDataFile` now returns a strict ByteString. + + Export `readDataFileUTF8` which returns a String, like the + old `readDataFile`. + + Export `fetchItem` and `openURL`. + + * `Text.Pandoc.ImageSize`: Use strict, not lazy bytestrings. + Removed `readImageSize`. + + * `Text.Pandoc.UTF8`: Export `encodePath`, `decodePath`, + `decodeArg`, `toString`, `fromString`, `toStringLazy`, + `fromStringLazy`. + + * `Text.Pandoc.UTF8` is now an exposed module. + + * `Text.Pandoc.Biblio`: + + + csl parameter now a `String` rather than a `FilePath`. + + Changed type of `processBiblio`. It is no longer in the IO monad. + It now takes a `Maybe Style` argument rather than parameters for CSL + and abbrev filenames. (`pandoc.hs` now calls the functions to parse + the style file and add abbreviations.) + + * Markdown reader now exports `readMarkdownWithWarnings`. + + * `Text.Pandoc.RTF` now exports `writeRTFWithEmbeddedImages` instead of + `rtfEmbedImage`. + + [bug fixes] + + * Make `--ascii` work properly with `--self-contained`. Closes #568. + + * Markdown reader: + + + Fixed link parser to avoid exponential slowdowns. Closes #620. + Previously the parser would hang on input like this: + + [[[[[[[[[[[[[[[[[[hi + + We fixed this by making the link parser parser characters + between balanced brackets (skipping brackets in inline code spans), + then parsing the result as an inline list. One change is that + + [hi *there]* bud](/url) + + is now no longer parsed as a link. But in this respect pandoc behaved + differently from most other implementations anyway, so that seems okay. + + + Look for raw html/latex blocks before tables. + Otherwise the following gets parsed as a table: + + \begin{code} + -------------- + -- My comment. + \end{code} + + Closes #578. + + * RST reader: + + + Added support for `:target:` on `.. image::` blocks + and substitutions. + + Field list fixes: + + - Fixed field lists items with body beginning after a new line + (Denis Laxalde). + - Allow any char but ':' in names of field lists in RST reader + (Denis Laxalde). + - Don't allow line breaks in field names. + - Require whitespace after field list field names. + - Don't create empty definition list for metadata field lists. + Previously a field list consisting only of metadata fields (author, + title, date) would be parsed as an empty DefinitionList, which is + not legal in LaTeX and not needed in any format. + + + Don't recognize inline-markup starts inside words. + For example, `2*2 = 4*1` should not contain an emphasized + section. Added test case for "Literal symbols". Closes #569. + + Allow dashes as separator in simple tables. Closes #555. + + Added support for `container`, `compound`, `epigraph`, + `rubric`, `highlights`, `pull-quote`. + + Added support for `.. code::`. + + Made directive labels case-insensitive. + + Removed requirement that directives begin at left margin. + This was (correctly) not in earlier releases; docutils doesn't + make the requirement. + + Added support for `replace::` and `unicode::` substitutions. + + Ignore unknown interpreted roles. + + Renamed image parser to `subst`, since it now handles all + substitution references. + + * Textile reader: + + + Allow newlines before pipes in table. Closes #654. + + Fixed bug with list items containing line breaks. + Now pandoc correctly handles hard line breaks inside list items. + Previously they broke list parsing. + + Implemented comment blocks. + + Fixed bug affected words ending in hyphen. + + Properly handle links with surrounding brackets. + Square brackets need to be used when the link isn't surrounded by + spaces or punctuation, or when the URL ending may be ambiguous. + Closes #564. + + Removed nullBlock. Better to know about parsing problems than + to skip stuff when we get stuck. + + Allow ID attributes on headers. + + Textile reader: Avoid parsing dashes as strikeout. + Previously the input + + text-- + text-- + text-- + text-- + + would be parsed with strikeouts rather than dashes. This fixes + the problem by requiring that a strikeout delimiting - not be + followed by a -. Closes #631. + + Expanded list of `stringBreakers`. + This fixes a bug on input like "(_hello_)" which should + be a parenthesized emphasized "hello". + The new list is taken from the PHP source of textile 2.4. + + Fixed autolinks. Previously the textile reader and writer + incorrectly implented RST-style autolinks for URLs and email + addresses. This has been fixed. Now an autolink is done this way: + `"$":http://myurl.com`. + + Fixed footnotes bug in textile. This affected notes occuring + before punctuation, e.g. `foo[1].`. Closes #518. + + * LaTeX reader: + + + Better handling of citation commands. + + Better handling of `\noindent`. + + Added a 'try' in rawLaTeXBlock, so we can handle `\begin` without `{`. + Closes #622. + + Made `rawLaTeXInline` try to parse block commands as well. This + is usually what we want, given how `rawLaTeXInline` is used in + the markdown and textile readers. If a block-level LaTeX command + is used in the middle of a paragraph (e.g. `\subtitle` inside a title), + we can treat it as raw inline LaTeX. + + Handle \slash command. Closes #605. + + Basic `\enquote` support. + + Fixed parsing of paragraphs beginning with a group. Closes #606. + + Use curly quotes for bare straight quotes. + + Support obeylines environment. Closes #604. + + Guard against "begin", "end" in inlineCommand and + blockCommand. + + Better error messages for environments. Now it should tell you that + it was looking for \end{env}, instead of giving "unknown parse error." + + * HTML reader: + + + Added HTML 5 tags to list of block-level tags. + + HTML reader: Fixed bug in `htmlBalanced`, which + caused hangs in parsing certain markdown input using + strict mode. + + Parse `<q>` as `Quoted DoubleQuote`. + + Handle nested `<q>` tags properly. + + Modified `htmlTag` for fewer false positives. + A tag must start with `<` followed by `!`,`?`, `/`, or a letter. + This makes it more useful in the wikimedia and markdown parsers. + + * DocBook reader: Support title in "figure" element. Closes #650. + + * MediaWiki writer: + + + Remove newline after `<br/>` in translation of `LineBreak` + There's no particular need for a newline (other than making the + generated MediaWiki source look nice to a human), and in fact + sometimes it is incorrect: in particular, inside an enumeration, list + items cannot have embedded newline characters. (Brent Yorgey) + + Use `<code>` not `<tt>` for Code. + + * Man writer: Escape `-` as `\-`. + Unescaped `-`'s become hyphens, while `\-`'s are left as ascii minus + signs. That is preferable for use with command-line options. See + <http://lintian.debian.org/tags/hyphen-used-as-minus-sign.html>. Thanks + to Andrea Bolognani for bringing the issue to our attention. + + * RST writer: + + + Improved line block output. Use nonbreaking spaces for + initial indent (otherwise lost in HTML and LaTeX). + Allow multiple paragraphs in a single line block. + Allow soft breaks w continuations in line blocks. + + Properly handle images with no alt text. Closes #678. + + Fixed bug with links with duplicate text. We now (a) use anonymous + links for links with inline URLs, and (b) use an inline link instead + of a reference link if the reference link would require a label that + has already been used for a different link. Closes #511. + + Fixed hyperlinked images. Closes #611. Use `:target:` + field when you have a simple linked image. + + Don't add `:align: center` to figures. + + * Texinfo writer: Fixed internal cross-references. + Now we insert anchors after each header, and use `@ref` instead of `@uref` + for links. Commas are now escaped as `@comma{}` only when needed; + previously all commas were escaped. (This change is needed, in part, + because `@ref` commands must be followed by a real comma or period.) Also + insert a blank line in from of `@verbatim` environments. + + * DocBook writer: + + + Made --id-prefix work in DocBook as well as HTML. + Closes #607. + + Don't include empty captions in figures. Closes #581. + + * LaTeX writer: + + + Use `\hspace*` for nonbreaking space after line break, + since `~` spaces after a line break are just ignored. + Closes #687. + + Don't escape `_` in URLs or hyperref identifiers. + + Properly escape strings inside \url{}. Closes #576. + + Use `[fragile]` only for slides containing code rendered + using listings. Closes #649. + + Escape `|` as `\vert` in LaTeX math. This avoids a clash with + highlighting-kate's macros, which redefine `|` as a short verbatim + delimiter. Thanks to Björn Peemöller for raising this issue. + + Use minipage rather than parbox for block containers in tables. + This allows verbatim code to be included in grid tables. + Closes #663. + + Prevent paragraphs containing only linebreaks or spaces. + + * HTML writer: + + + Included `highlighting-css` for code spans, too. + Previously it was only included if used in a code block. Closes #653. + + Improved line breaks with `<dd>` tags. We now put a newline between + `</dd>` and `<dd>` when there are multiple definitions. + + Changed mathjax cdn url so it doesn't use https. (This caused + problems when used with `--self-contained`.) See #609. + + * EPUB writer: + + + `--number-sections` now works properly. + + Don't strip meta and link elements in epub metadata. + Patch from aberrancy. Closes #589. + + Fixed a couple validation bugs. + + Use ch001, ch002, etc. for chapter filenames. This improves sorting + of chapters in some readers, which apparently sort ch2 after ch10. + Closes #610. + + * ODT writer: properly set title property (Arlo O'Keeffe). + + * Docx writer: + + + Fixed bug with nested lists. Previously a list like + + 1. one + - a + - b + 2. two + + would come out with a bullet instead of "2." + Thanks to Russell Allen for reporting the bug. + + Use `w:cr` in `w:r` instead of `w:br` for linebreaks. + This seems to fix a problem viewing pandoc-generated + docx files in LibreOffice. + + Use integer ids for bookmarks. Closes #626. + + Added nsid to abstractNum elements. This helps when merging + word documents with numbered or bulleted lists. Closes #627. + + Use separate footnotes.xml for notes. + This seems to help LibreOffice convert the file, even though + it was valid docx before. Closes #637. + + Use rIdNN identifiers for r:embed in images. + + Avoid reading image files again when we've already processed them. + + Fixed typo in `referenc.docx` that prevented image captions from + working. Thanks to Huashan Chen. + + * `Text.Pandoc.Parsing`: + + + Fixed bug in `withRaw`, which didn't correctly handle the case + where nothing is parsed. + + Made `emailAddress` parser more correct. Now it is based on RFC 822, + though it still doesn't implement quoted strings in email addresses. + + Revised URI parser. It now allows many more schemes, allows + uppercase URIs, and better handles trailing punctuation and + trailing slashes in bare URIs. Added many tests. + + Simplified and improved singleQuoteStart. This makes `'s'`, `'l'`, + etc. parse properly. Formerly we had some English-centric heuristics, + but they are no longer needed. Closes #698. + + * `Text.Pandoc.Pretty`: Added wide punctuation range to `charWidth`. + This fixes bug with Chinese commas in markdown and reST tables, and + a bug that caused combining characters to be dropped. + + * `Text.Pandoc.MIME`: Added MIME types for .wof and .eot. Closes #640. + + * `Text.Pandoc.Biblio`: + + + Run `mvPunc` and `deNote` on metadata too. + This fixed a bug with notes on titles using footnote styles. + + Fixed bug in fetching CSL files from CSL data directory. + + * `pandoc.hs`: Give correct value to `writerSourceDirectory` when a URL + is provided. It should be the URL up to the path. + + * Fixed/simplified diff output for tests. + Biblio: Make sure mvPunc and deNote run on metadata too. + This fixed a bug with notes on titles using footnote styles. + + [under the hood improvements] + + * We no longer depend on `utf8-string`. Instead we use functions + defined in `Text.Pandoc.UTF8` that use `Data.Text`'s conversions. + + * Use `safeRead` instead of using `reads` directly (various modules). + + * "Implicit figures" (images alone in a paragraph) are now handled + differently. The markdown reader gives their titles the prefix `fig:`; the + writers look for this before treating the image as a figure. Though this + is a bit of a hack, it has two advantages: (i) implicit figures can be + limited to the markdown reader, and (ii) they can be deactivated by turning + off the `implicit_figures` extension. + + * `catch` from `Control.Exception` is now used instead of the + old Preface `catch`. + + * `Text.Pandoc.Shared`: Improved algorithm for `normalizeSpaces` + and `oneOfStrings` (which is now non-backtracking). + + * `Text.Pandoc.Biblio`: Remove workaround for `toCapital`. + Now citeproc-hs is fixed upstream, so this is no longer needed. + Closes #531. + + * Textile reader: Improved speed of `hyphenedWords`. + This speeds up the textile reader by about a factor of 4. + + * Use `Text.Pandoc.Builder` in RST reader, for more flexibility, + better performance, and automatic normalization. + + * Major rewrite of markdown reader: + + + Use `Text.Pandoc.Builder` instead of lists. This also + means that everything is normalized automatically. + + Move to a one-pass parsing strategy, returning values in the reader + monad, which are then run (at the end of parsing) against the final + parser state. + + * In HTML writer, we now use `toHtml` instead of pre-escaping. + We work around the problem that blaze-html unnecessarily escapes `'` + by pre-escaping just the `'` characters, instead of the whole string. + If blaze-html later stops escaping `'` characters, we can simplify + `strToHtml` to `toHtml`. Closes #629. + + * Moved code for embedding images in RTFs from `pandoc.hs` to the + RTF writer (which now exports `writeRTFWithEmbeddedImages`). + + * Moved citation processing from `pandoc.hs` into the readers. + This makes things more convenient for library users. + + * The man pages are now built by an executable `make-pandoc-man-pages`, + which has its own stanza in the cabal file so that dependencies can be + handled by Cabal. Special treatment in `Setup.hs` ensures that this + executable never gets installed; it is only used to create the man pages. + + * The cabal file has been modified so that the pandoc library is used + in building the pandoc executable. (This required moving `pandoc.hs` + from `src` to `.`.) This cuts compile time in half. + + * `-O2` is no longer used in building pandoc. The performance improvement + it yields is so slight that it is not worth it. (Measured with + benchmarks on ghc 7.4.) + + * The `executable` and `library` flags have been removed. + + * `-threaded` has been removed from ghc-options. + + * Version bounds of dependencies have been raised, and the + `blaze_html_0_5` flag now defaults to True. Pandoc now compiles on + GHC 7.6. + + * We now require base >= 4.2. + + * Integrated the benchmark program into cabal. One can now do: + + cabal configure --enable-benchmarks && cabal build + cabal bench --benchmark-option='markdown' --benchmark-option='-s 20' + + The benchmark now uses README + testsuite, so benchmark results + from older versions aren't comparable. + + * Integrated test suite with cabal. + To run tests, configure with `--enable-tests`, then `cabal test`. + You can specify particular tests using `--test-options='-t markdown'`. + No output is shown unless tests fail. The Haskell test modules + have been moved from `src/` to `tests/`. + + * Moved all data files and templates to the `data/` subdirectory. + + * Added an `embed_data_files` cabal flag. This causes all + data files to be embedded in the binary, so that the binary + is self-sufficient and can be relocated anywhere, copied on + a USB key, etc. The Windows installer now uses this. + (Since we no longer have the option to build the executable + without the library, this is the only way to get a relocatable + binary on Windows.) + + * Removed pcre3.dll from windows package. + It isn't needed unless highlighting-kate is compilled with the + `pcre-light` flag. By default, regex-prce-builtin is used. - * Modified modules to compile with GHC 7.6 and latest version of time - package. pandoc (1.9.4.2) diff --git a/default.csl b/data/default.csl index dea707114..dea707114 100644 --- a/default.csl +++ b/data/default.csl diff --git a/dzslides/template.html b/data/dzslides/template.html index f2fb64b38..f2fb64b38 100644 --- a/dzslides/template.html +++ b/data/dzslides/template.html diff --git a/epub.css b/data/epub.css index 6737f5cc7..3d9c59395 100644 --- a/epub.css +++ b/data/epub.css @@ -1,12 +1,12 @@ /* This defines styles and classes used in the book */ body { margin-left: 5%; margin-right: 5%; margin-top: 5%; margin-bottom: 5%; text-align: justify; font-size: medium; } code { font-family: monospace; } -h1 { text-align: center; } -h2 { text-align: center; } -h3 { text-align: center; } -h4 { text-align: center; } -h5 { text-align: center; } -h6 { text-align: center; } +h1 { text-align: left; } +h2 { text-align: left; } +h3 { text-align: left; } +h4 { text-align: left; } +h5 { text-align: left; } +h6 { text-align: left; } h1.title { } h2.author { } h3.date { } diff --git a/data/reference.docx b/data/reference.docx Binary files differnew file mode 100644 index 000000000..fd37a8b58 --- /dev/null +++ b/data/reference.docx diff --git a/reference.odt b/data/reference.odt Binary files differindex 6307119d3..6307119d3 100644 --- a/reference.odt +++ b/data/reference.odt diff --git a/s5/default/blank.gif b/data/s5/default/blank.gif Binary files differindex 75b945d25..75b945d25 100644 --- a/s5/default/blank.gif +++ b/data/s5/default/blank.gif diff --git a/s5/default/bodybg.gif b/data/s5/default/bodybg.gif Binary files differindex 5f448a16f..5f448a16f 100644 --- a/s5/default/bodybg.gif +++ b/data/s5/default/bodybg.gif diff --git a/s5/default/framing.css b/data/s5/default/framing.css index 14d8509e9..14d8509e9 100644 --- a/s5/default/framing.css +++ b/data/s5/default/framing.css diff --git a/s5/default/iepngfix.htc b/data/s5/default/iepngfix.htc index bba2db756..bba2db756 100644 --- a/s5/default/iepngfix.htc +++ b/data/s5/default/iepngfix.htc diff --git a/s5/default/opera.css b/data/s5/default/opera.css index 9e9d2a3c5..9e9d2a3c5 100644 --- a/s5/default/opera.css +++ b/data/s5/default/opera.css diff --git a/s5/default/outline.css b/data/s5/default/outline.css index 62db519ed..62db519ed 100644 --- a/s5/default/outline.css +++ b/data/s5/default/outline.css diff --git a/s5/default/pretty.css b/data/s5/default/pretty.css index a87b24375..a87b24375 100644 --- a/s5/default/pretty.css +++ b/data/s5/default/pretty.css diff --git a/s5/default/print.css b/data/s5/default/print.css index 4a3554ddd..4a3554ddd 100644 --- a/s5/default/print.css +++ b/data/s5/default/print.css diff --git a/s5/default/s5-core.css b/data/s5/default/s5-core.css index 86444e041..86444e041 100644 --- a/s5/default/s5-core.css +++ b/data/s5/default/s5-core.css diff --git a/s5/default/slides.css b/data/s5/default/slides.css index 0786d7dbd..0786d7dbd 100644 --- a/s5/default/slides.css +++ b/data/s5/default/slides.css diff --git a/s5/default/slides.js b/data/s5/default/slides.js index 38fe8531c..38fe8531c 100644 --- a/s5/default/slides.js +++ b/data/s5/default/slides.js diff --git a/slideous/slideous.css b/data/slideous/slideous.css index 7d6057069..7d6057069 100644 --- a/slideous/slideous.css +++ b/data/slideous/slideous.css diff --git a/slideous/slideous.js b/data/slideous/slideous.js index 3e7a63d4a..3e7a63d4a 100644 --- a/slideous/slideous.js +++ b/data/slideous/slideous.js diff --git a/slidy/graphics/fold-dim.gif b/data/slidy/graphics/fold-dim.gif Binary files differindex 346fcbf3e..346fcbf3e 100644 --- a/slidy/graphics/fold-dim.gif +++ b/data/slidy/graphics/fold-dim.gif diff --git a/slidy/graphics/fold.gif b/data/slidy/graphics/fold.gif Binary files differindex 133e594fd..133e594fd 100644 --- a/slidy/graphics/fold.gif +++ b/data/slidy/graphics/fold.gif diff --git a/slidy/graphics/nofold-dim.gif b/data/slidy/graphics/nofold-dim.gif Binary files differindex 996fb5eda..996fb5eda 100644 --- a/slidy/graphics/nofold-dim.gif +++ b/data/slidy/graphics/nofold-dim.gif diff --git a/slidy/graphics/unfold-dim.gif b/data/slidy/graphics/unfold-dim.gif Binary files differindex bee567117..bee567117 100644 --- a/slidy/graphics/unfold-dim.gif +++ b/data/slidy/graphics/unfold-dim.gif diff --git a/slidy/graphics/unfold.gif b/data/slidy/graphics/unfold.gif Binary files differindex 0753ae4d2..0753ae4d2 100644 --- a/slidy/graphics/unfold.gif +++ b/data/slidy/graphics/unfold.gif diff --git a/slidy/scripts/slidy.js.gz b/data/slidy/scripts/slidy.js.gz Binary files differindex 72bbd63d0..72bbd63d0 100644 --- a/slidy/scripts/slidy.js.gz +++ b/data/slidy/scripts/slidy.js.gz diff --git a/slidy/styles/slidy.css b/data/slidy/styles/slidy.css index 0197e64d0..0197e64d0 100644 --- a/slidy/styles/slidy.css +++ b/data/slidy/styles/slidy.css diff --git a/templates/default.asciidoc b/data/templates/default.asciidoc index 3e30ceef8..3e30ceef8 100644 --- a/templates/default.asciidoc +++ b/data/templates/default.asciidoc diff --git a/templates/default.beamer b/data/templates/default.beamer index fffd02d39..c5562a1a1 100644 --- a/templates/default.beamer +++ b/data/templates/default.beamer @@ -41,25 +41,44 @@ $endif$ $if(verbatim-in-note)$ \usepackage{fancyvrb} $endif$ -$if(fancy-enums)$ -\usepackage{enumerate} -$endif$ $if(tables)$ -\usepackage{ctable} -\usepackage{float} % provides the H option for float placement +\usepackage{longtable} $endif$ $if(url)$ \usepackage{url} $endif$ $if(graphics)$ \usepackage{graphicx} +\makeatletter +\def\ScaleIfNeeded{% + \ifdim\Gin@nat@width>\linewidth + \linewidth + \else + \Gin@nat@width + \fi +} +\makeatother +\setkeys{Gin}{width=\ScaleIfNeeded} $endif$ + % Comment these out if you don't want a slide with just the % part/section/subsection/subsubsection title: -\AtBeginPart{\frame{\partpage}} -\AtBeginSection{\frame{\sectionpage}} -\AtBeginSubsection{\frame{\subsectionpage}} -\AtBeginSubsubsection{\frame{\subsubsectionpage}} +\AtBeginPart{ + \let\insertpartnumber\relax + \let\partname\relax + \frame{\partpage} +} +\AtBeginSection{ + \let\insertsectionnumber\relax + \let\sectionname\relax + \frame{\sectionpage} +} +\AtBeginSubsection{ + \let\insertsubsectionnumber\relax + \let\subsectionname\relax + \frame{\subsectionpage} +} + $if(strikeout)$ \usepackage[normalem]{ulem} % avoid problems with \sout in headers with hyperref: diff --git a/templates/default.context b/data/templates/default.context index 54cc8ea09..6372cd0b4 100644 --- a/templates/default.context +++ b/data/templates/default.context @@ -9,7 +9,7 @@ $endif$ % Enable hyperlinks \setupinteraction[state=start, color=middleblue] -\setuppapersize [letter][letter] +\setuppapersize [$if(papersize)$$papersize$$else$letter$endif$][$if(papersize)$$papersize$$else$letter$endif$] \setuplayout [width=middle, backspace=1.5in, cutspace=1.5in, height=middle, topspace=0.75in, bottomspace=0.75in] @@ -69,6 +69,7 @@ $for(include-before)$ $include-before$ $endfor$ $if(toc)$ +\placelist[$placelist$] \placecontent $endif$ diff --git a/templates/default.docbook b/data/templates/default.docbook index e1c8e0134..e1c8e0134 100644 --- a/templates/default.docbook +++ b/data/templates/default.docbook diff --git a/templates/default.dzslides b/data/templates/default.dzslides index 48e3e8027..d252db197 100644 --- a/templates/default.dzslides +++ b/data/templates/default.dzslides @@ -7,7 +7,8 @@ $endfor$ $if(date-meta)$ <meta name="dcterms.date" content="$date-meta$" /> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> $if(highlighting-css)$ <style type="text/css"> $highlighting-css$ diff --git a/data/templates/default.epub b/data/templates/default.epub new file mode 100644 index 000000000..5e849d784 --- /dev/null +++ b/data/templates/default.epub @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ xml:lang="$lang$"$endif$> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta http-equiv="Content-Style-Type" content="text/css" /> + <meta name="generator" content="pandoc" /> + <title>$pagetitle$</title> +$if(highlighting-css)$ + <style type="text/css"> +$highlighting-css$ + </style> +$endif$ +$for(css)$ + <link rel="stylesheet" type="text/css" href="$css$" /> +$endfor$ +</head> +<body> +$if(titlepage)$ + <h1 class="title">$title$</h1> +$for(author)$ + <h2 class="author">$author$</h2> +$endfor$ +$if(date)$ + <h3 class="date">$date$</h3> +$endif$ +$else$ +$body$ +$endif$ +</body> +</html> + diff --git a/data/templates/default.epub3 b/data/templates/default.epub3 new file mode 100644 index 000000000..ba7516052 --- /dev/null +++ b/data/templates/default.epub3 @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"$if(lang)$ xml:lang="$lang$"$endif$> +<head> + <meta charset="utf-8" /> + <meta name="generator" content="pandoc" /> + <title>$pagetitle$</title> +$if(quotes)$ + <style type="text/css"> + q { quotes: "“" "”" "‘" "’"; } + </style> +$endif$ +$if(highlighting-css)$ + <style type="text/css"> +$highlighting-css$ + </style> +$endif$ +$for(css)$ + <link rel="stylesheet" href="$css$" /> +$endfor$ +</head> +<body> +$if(titlepage)$ + <h1 class="title">$title$</h1> +$for(author)$ + <h2 class="author">$author$</h2> +$endfor$ +$if(date)$ + <h3 class="date">$date$</h3> +$endif$ +$else$ +$body$ +$endif$ +</body> +</html> + diff --git a/templates/default.html b/data/templates/default.html index c231b8fa5..b7b74c223 100644 --- a/templates/default.html +++ b/data/templates/default.html @@ -10,7 +10,11 @@ $endfor$ $if(date-meta)$ <meta name="date" content="$date-meta$" /> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> +$if(quotes)$ + <style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style> +$endif$ $if(highlighting-css)$ <style type="text/css"> $highlighting-css$ diff --git a/templates/default.html5 b/data/templates/default.html5 index 800f8a626..720b2fc62 100644 --- a/templates/default.html5 +++ b/data/templates/default.html5 @@ -9,14 +9,13 @@ $endfor$ $if(date-meta)$ <meta name="dcterms.date" content="$date-meta$"> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> $if(quotes)$ - <style type="text/css"> - q { quotes: "“" "”" "‘" "’"; } - </style> + <style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style> $endif$ $if(highlighting-css)$ <style type="text/css"> diff --git a/templates/default.latex b/data/templates/default.latex index e92ced331..c6d60484e 100644 --- a/templates/default.latex +++ b/data/templates/default.latex @@ -1,4 +1,4 @@ -\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$lang$,$endif$]{$documentclass$} +\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$lang$,$endif$$if(papersize)$$papersize$,$endif$]{$documentclass$} \usepackage[T1]{fontenc} \usepackage{lmodern} \usepackage{amssymb,amsmath} @@ -6,6 +6,8 @@ \usepackage{fixltx2e} % provides \textsubscript % use microtype if available \IfFileExists{microtype.sty}{\usepackage{microtype}}{} +% use upquote if available, for straight quotes in verbatim environments +\IfFileExists{upquote.sty}{\usepackage{upquote}}{} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex \usepackage[utf8]{inputenc} $if(euro)$ @@ -56,18 +58,8 @@ $endif$ $if(verbatim-in-note)$ \usepackage{fancyvrb} $endif$ -$if(fancy-enums)$ -% Redefine labelwidth for lists; otherwise, the enumerate package will cause -% markers to extend beyond the left margin. -\makeatletter\AtBeginDocument{% - \renewcommand{\@listi} - {\setlength{\labelwidth}{4em}} -}\makeatother -\usepackage{enumerate} -$endif$ $if(tables)$ -\usepackage{ctable} -\usepackage{float} % provides the H option for float placement +\usepackage{longtable} $endif$ $if(graphics)$ \usepackage{graphicx} @@ -145,6 +137,7 @@ $endfor$ $if(toc)$ { \hypersetup{linkcolor=black} +\setcounter{tocdepth}{$toc-depth$} \tableofcontents } $endif$ diff --git a/templates/default.man b/data/templates/default.man index ff86c8ca6..ff86c8ca6 100644 --- a/templates/default.man +++ b/data/templates/default.man diff --git a/templates/default.markdown b/data/templates/default.markdown index d500d3384..95d7e52cc 100644 --- a/templates/default.markdown +++ b/data/templates/default.markdown @@ -1,7 +1,5 @@ $if(titleblock)$ -% $title$ -% $for(author)$$author$$sep$; $endfor$ -% $date$ +$titleblock$ $endif$ $for(header-includes)$ diff --git a/templates/default.mediawiki b/data/templates/default.mediawiki index 5d210fa7d..5d210fa7d 100644 --- a/templates/default.mediawiki +++ b/data/templates/default.mediawiki diff --git a/templates/default.opendocument b/data/templates/default.opendocument index 4135cdea7..4135cdea7 100644 --- a/templates/default.opendocument +++ b/data/templates/default.opendocument diff --git a/templates/default.org b/data/templates/default.org index eaaa17533..eaaa17533 100644 --- a/templates/default.org +++ b/data/templates/default.org diff --git a/templates/default.plain b/data/templates/default.plain index 06ecbd3a6..95d7e52cc 100644 --- a/templates/default.plain +++ b/data/templates/default.plain @@ -1,7 +1,5 @@ $if(titleblock)$ -$title$ -$for(author)$$author$$sep$; $endfor$ -$date$ +$titleblock$ $endif$ $for(header-includes)$ diff --git a/templates/default.rst b/data/templates/default.rst index 3b28cbf51..ca9d2833d 100644 --- a/templates/default.rst +++ b/data/templates/default.rst @@ -27,6 +27,7 @@ $include-before$ $endfor$ $if(toc)$ .. contents:: + :depth: $toc-depth$ .. $endif$ diff --git a/templates/default.rtf b/data/templates/default.rtf index 833e19844..59e132b3f 100644 --- a/templates/default.rtf +++ b/data/templates/default.rtf @@ -17,6 +17,9 @@ $endif$ $if(spacer)$ {\pard \ql \f0 \sa180 \li0 \fi0 \par} $endif$ +$if(toc)$ +$toc$ +$endif$ $for(include-before)$ $include-before$ $endfor$ diff --git a/templates/default.s5 b/data/templates/default.s5 index 03008df88..532c164cf 100644 --- a/templates/default.s5 +++ b/data/templates/default.s5 @@ -10,7 +10,8 @@ $endfor$ $if(date-meta)$ <meta name="date" content="$date-meta$" /> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> <!-- configuration parameters --> <meta name="defaultView" content="slideshow" /> <meta name="controlVis" content="hidden" /> diff --git a/templates/default.slideous b/data/templates/default.slideous index a0f2e258c..db16a9693 100644 --- a/templates/default.slideous +++ b/data/templates/default.slideous @@ -12,7 +12,8 @@ $endfor$ $if(date-meta)$ <meta name="date" content="$date-meta$" /> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> $if(highlighting-css)$ <style type="text/css"> $highlighting-css$ diff --git a/templates/default.slidy b/data/templates/default.slidy index 0a74ca7f5..e54e95510 100644 --- a/templates/default.slidy +++ b/data/templates/default.slidy @@ -12,7 +12,8 @@ $endfor$ $if(date-meta)$ <meta name="date" content="$date-meta$" /> $endif$ - <title>$if(title-prefix)$$title-prefix$ - $endif$$if(pagetitle)$$pagetitle$$endif$</title> + <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> + <style type="text/css">code{white-space: pre;}</style> $if(highlighting-css)$ <style type="text/css"> $highlighting-css$ diff --git a/templates/default.texinfo b/data/templates/default.texinfo index 458d4fdda..458d4fdda 100644 --- a/templates/default.texinfo +++ b/data/templates/default.texinfo diff --git a/templates/default.textile b/data/templates/default.textile index 69bd05b56..69bd05b56 100644 --- a/templates/default.textile +++ b/data/templates/default.textile diff --git a/man/make-pandoc-man-pages.hs b/man/make-pandoc-man-pages.hs index cfefa7aa3..52e2741f1 100644 --- a/man/make-pandoc-man-pages.hs +++ b/man/make-pandoc-man-pages.hs @@ -23,7 +23,7 @@ main = do unless (null ds1 && null ds2) $ do rmContents <- UTF8.readFile "README" - let (Pandoc meta blocks) = readMarkdown defaultParserState rmContents + let (Pandoc meta blocks) = readMarkdown def rmContents let manBlocks = removeSect [Str "Wrappers"] $ removeSect [Str "Pandoc's",Space,Str "markdown"] blocks let syntaxBlocks = extractSect [Str "Pandoc's",Space,Str "markdown"] blocks @@ -43,8 +43,7 @@ makeManPage verbose page meta blocks = do writeManPage :: FilePath -> String -> Pandoc -> IO () writeManPage page templ doc = do - let opts = defaultWriterOptions{ - writerStandalone = True + let opts = def{ writerStandalone = True , writerTemplate = templ } let manPage = writeMan opts $ bottomUp (concatMap removeLinks) $ @@ -56,7 +55,7 @@ removeLinks (Link l _) = l removeLinks x = [x] capitalizeHeaders :: Block -> Block -capitalizeHeaders (Header 1 xs) = Header 1 $ bottomUp capitalize xs +capitalizeHeaders (Header 1 attr xs) = Header 1 attr $ bottomUp capitalize xs capitalizeHeaders x = x capitalize :: Inline -> Inline @@ -64,22 +63,22 @@ capitalize (Str xs) = Str $ map toUpper xs capitalize x = x removeSect :: [Inline] -> [Block] -> [Block] -removeSect ils (Header 1 x:xs) | normalize x == normalize ils = +removeSect ils (Header 1 _ x:xs) | normalize x == normalize ils = dropWhile (not . isHeader1) xs removeSect ils (x:xs) = x : removeSect ils xs removeSect _ [] = [] extractSect :: [Inline] -> [Block] -> [Block] -extractSect ils (Header 1 z:xs) | normalize z == normalize ils = +extractSect ils (Header 1 _ z:xs) | normalize z == normalize ils = bottomUp promoteHeader $ takeWhile (not . isHeader1) xs - where promoteHeader (Header n x) = Header (n-1) x + where promoteHeader (Header n attr x) = Header (n-1) attr x promoteHeader x = x extractSect ils (x:xs) = extractSect ils xs extractSect _ [] = [] isHeader1 :: Block -> Bool -isHeader1 (Header 1 _) = True -isHeader1 _ = False +isHeader1 (Header 1 _ _ ) = True +isHeader1 _ = False -- | Returns a list of 'dependencies' that have been modified after 'file'. diff --git a/man/man1/pandoc.1 b/man/man1/pandoc.1 index 86ef6ae7c..ce9ece429 100644 --- a/man/man1/pandoc.1 +++ b/man/man1/pandoc.1 @@ -1,29 +1,29 @@ -.TH PANDOC 1 "January 27, 2012" "Pandoc" +.TH PANDOC 1 "January 19, 2013" "Pandoc" .SH NAME pandoc - general markup converter .SH SYNOPSIS .PP -pandoc [\f[I]options\f[]] [\f[I]input-file\f[]]... +pandoc [\f[I]options\f[]][*input\-file*]... .SH DESCRIPTION .PP Pandoc is a Haskell library for converting from one markup format to -another, and a command-line tool that uses this library. +another, and a command\-line tool that uses this library. It can read markdown and (subsets of) Textile, reStructuredText, HTML, -LaTeX, and DocBook XML; and it can write plain text, markdown, -reStructuredText, XHTML, HTML 5, LaTeX (including beamer slide shows), -ConTeXt, RTF, DocBook XML, OpenDocument XML, ODT, Word docx, GNU -Texinfo, MediaWiki markup, EPUB, Textile, groff man pages, Emacs -Org-Mode, AsciiDoc, and Slidy, Slideous, DZSlides, or S5 HTML slide -shows. +LaTeX, MediaWiki markup, and DocBook XML; and it can write plain text, +markdown, reStructuredText, XHTML, HTML 5, LaTeX (including beamer slide +shows), ConTeXt, RTF, DocBook XML, OpenDocument XML, ODT, Word docx, GNU +Texinfo, MediaWiki markup, EPUB (v2 or v3), FictionBook2, Textile, groff +man pages, Emacs Org\-Mode, AsciiDoc, and Slidy, Slideous, DZSlides, or +S5 HTML slide shows. It can also produce PDF output on systems where LaTeX is installed. .PP Pandoc\[aq]s enhanced version of markdown includes syntax for footnotes, -tables, flexible ordered lists, definition lists, delimited code blocks, +tables, flexible ordered lists, definition lists, fenced code blocks, superscript, subscript, strikeout, title blocks, automatic tables of contents, embedded LaTeX math, citations, and markdown inside HTML block elements. (These enhancements, described below under Pandoc\[aq]s markdown, can be -disabled using the \f[C]--strict\f[] option.) +disabled using the \f[C]markdown_strict\f[] input or output format.) .PP In contrast to most existing tools for converting markdown to HTML, which use regex substitutions, Pandoc has a modular design: it consists @@ -34,18 +34,18 @@ Thus, adding an input or output format requires only adding a reader or writer. .SS Using \f[C]pandoc\f[] .PP -If no \f[I]input-file\f[] is specified, input is read from +If no \f[I]input\-file\f[] is specified, input is read from \f[I]stdin\f[]. -Otherwise, the \f[I]input-files\f[] are concatenated (with a blank line +Otherwise, the \f[I]input\-files\f[] are concatenated (with a blank line between each) and used as input. Output goes to \f[I]stdout\f[] by default (though output to -\f[I]stdout\f[] is disabled for the \f[C]odt\f[], \f[C]docx\f[], and -\f[C]epub\f[] output formats). -For output to a file, use the \f[C]-o\f[] option: +\f[I]stdout\f[] is disabled for the \f[C]odt\f[], \f[C]docx\f[], +\f[C]epub\f[], and \f[C]epub3\f[] output formats). +For output to a file, use the \f[C]\-o\f[] option: .IP .nf \f[C] -pandoc\ -o\ output.html\ input.txt +pandoc\ \-o\ output.html\ input.txt \f[] .fi .PP @@ -54,7 +54,7 @@ In this case pandoc will fetch the content using HTTP: .IP .nf \f[C] -pandoc\ -f\ html\ -t\ markdown\ http://www.fsf.org +pandoc\ \-f\ html\ \-t\ markdown\ http://www.fsf.org \f[] .fi .PP @@ -62,16 +62,16 @@ If multiple input files are given, \f[C]pandoc\f[] will concatenate them all (with blank lines between them) before parsing. .PP The format of the input and output can be specified explicitly using -command-line options. -The input format can be specified using the \f[C]-r/--read\f[] or -\f[C]-f/--from\f[] options, the output format using the -\f[C]-w/--write\f[] or \f[C]-t/--to\f[] options. +command\-line options. +The input format can be specified using the \f[C]\-r/\-\-read\f[] or +\f[C]\-f/\-\-from\f[] options, the output format using the +\f[C]\-w/\-\-write\f[] or \f[C]\-t/\-\-to\f[] options. Thus, to convert \f[C]hello.txt\f[] from markdown to LaTeX, you could type: .IP .nf \f[C] -pandoc\ -f\ markdown\ -t\ latex\ hello.txt +pandoc\ \-f\ markdown\ \-t\ latex\ hello.txt \f[] .fi .PP @@ -79,13 +79,13 @@ To convert \f[C]hello.html\f[] from html to markdown: .IP .nf \f[C] -pandoc\ -f\ html\ -t\ markdown\ hello.html +pandoc\ \-f\ html\ \-t\ markdown\ hello.html \f[] .fi .PP -Supported output formats are listed below under the \f[C]-t/--to\f[] +Supported output formats are listed below under the \f[C]\-t/\-\-to\f[] option. -Supported input formats are listed below under the \f[C]-f/--from\f[] +Supported input formats are listed below under the \f[C]\-f/\-\-from\f[] option. Note that the \f[C]rst\f[], \f[C]textile\f[], \f[C]latex\f[], and \f[C]html\f[] readers are not complete; there are some constructs that @@ -98,7 +98,7 @@ Thus, for example, .IP .nf \f[C] -pandoc\ -o\ hello.tex\ hello.txt +pandoc\ \-o\ hello.tex\ hello.txt \f[] .fi .PP @@ -110,13 +110,13 @@ If no input file is specified (so that input comes from \f[I]stdin\f[]), or if the input files\[aq] extensions are unknown, the input format will be assumed to be markdown unless explicitly specified. .PP -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[C]iconv\f[]: +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[C]iconv\f[]: .IP .nf \f[C] -iconv\ -t\ utf-8\ input.txt\ |\ pandoc\ |\ iconv\ -f\ utf-8 +iconv\ \-t\ utf\-8\ input.txt\ |\ pandoc\ |\ iconv\ \-f\ utf\-8 \f[] .fi .SS Creating a PDF @@ -128,87 +128,107 @@ This is no longer needed, since \f[C]pandoc\f[] can now produce To produce a PDF, simply specify an output file with a \f[C]\&.pdf\f[] extension. Pandoc will create a latex file and use pdflatex (or another engine, see -\f[C]--latex-engine\f[]) to convert it to PDF: +\f[C]\-\-latex\-engine\f[]) to convert it to PDF: .IP .nf \f[C] -pandoc\ test.txt\ -o\ test.pdf +pandoc\ test.txt\ \-o\ test.pdf \f[] .fi .PP Production of a PDF requires that a LaTeX engine be installed (see -\f[C]--latex-engine\f[], below), and assumes that the following LaTeX +\f[C]\-\-latex\-engine\f[], below), and assumes that the following LaTeX packages are available: \f[C]amssymb\f[], \f[C]amsmath\f[], \f[C]ifxetex\f[], \f[C]ifluatex\f[], \f[C]listings\f[] (if the -\f[C]--listings\f[] option is used), \f[C]fancyvrb\f[], -\f[C]enumerate\f[], \f[C]ctable\f[], \f[C]url\f[], \f[C]graphicx\f[], -\f[C]hyperref\f[], \f[C]ulem\f[], \f[C]babel\f[] (if the \f[C]lang\f[] -variable is set), \f[C]fontspec\f[] (if \f[C]xelatex\f[] or -\f[C]lualatex\f[] is used as the LaTeX engine), \f[C]xltxtra\f[] and -\f[C]xunicode\f[] (if \f[C]xelatex\f[] is used). +\f[C]\-\-listings\f[] option is used), \f[C]fancyvrb\f[], +\f[C]longtable\f[], \f[C]url\f[], \f[C]graphicx\f[], \f[C]hyperref\f[], +\f[C]ulem\f[], \f[C]babel\f[] (if the \f[C]lang\f[] variable is set), +\f[C]fontspec\f[] (if \f[C]xelatex\f[] or \f[C]lualatex\f[] is used as +the LaTeX engine), \f[C]xltxtra\f[] and \f[C]xunicode\f[] (if +\f[C]xelatex\f[] is used). .SS \f[C]hsmarkdown\f[] .PP -A user who wants a drop-in replacement for \f[C]Markdown.pl\f[] may +A user who wants a drop\-in replacement for \f[C]Markdown.pl\f[] may create a symbolic link to the \f[C]pandoc\f[] executable called \f[C]hsmarkdown\f[]. When invoked under the name \f[C]hsmarkdown\f[], \f[C]pandoc\f[] will -behave as if the \f[C]--strict\f[] flag had been selected, and no -command-line options will be recognized. +behave as if invoked with +\f[C]\-f\ markdown_strict\ \-\-email\-obfuscation=references\f[], and +all command\-line options will be treated as regular arguments. However, this approach does not work under Cygwin, due to problems with its simulation of symbolic links. .SH OPTIONS .SS General options .TP -.B \f[C]-f\f[] \f[I]FORMAT\f[], \f[C]-r\f[] \f[I]FORMAT\f[], -\f[C]--from=\f[]\f[I]FORMAT\f[], \f[C]--read=\f[]\f[I]FORMAT\f[] +.B \f[C]\-f\f[] \f[I]FORMAT\f[], \f[C]\-r\f[] \f[I]FORMAT\f[], +\f[C]\-\-from=\f[]\f[I]FORMAT\f[], \f[C]\-\-read=\f[]\f[I]FORMAT\f[] Specify input format. \f[I]FORMAT\f[] can be \f[C]native\f[] (native Haskell), \f[C]json\f[] -(JSON version of native AST), \f[C]markdown\f[] (markdown), -\f[C]textile\f[] (Textile), \f[C]rst\f[] (reStructuredText), -\f[C]html\f[] (HTML), \f[C]docbook\f[] (DocBook XML), or \f[C]latex\f[] -(LaTeX). +(JSON version of native AST), \f[C]markdown\f[] (pandoc\[aq]s extended +markdown), \f[C]markdown_strict\f[] (original unextended markdown), +\f[C]markdown_phpextra\f[] (PHP Markdown Extra extended markdown), +\f[C]markdown_github\f[] (github extended markdown), \f[C]textile\f[] +(Textile), \f[C]rst\f[] (reStructuredText), \f[C]html\f[] (HTML), +\f[C]docbook\f[] (DocBook XML), \f[C]mediawiki\f[] (MediaWiki markup), +or \f[C]latex\f[] (LaTeX). If \f[C]+lhs\f[] is appended to \f[C]markdown\f[], \f[C]rst\f[], \f[C]latex\f[], the input will be treated as literate Haskell source: see Literate Haskell support, below. +Markdown syntax extensions can be individually enabled or disabled by +appending \f[C]+EXTENSION\f[] or \f[C]\-EXTENSION\f[] to the format +name. +So, for example, \f[C]markdown_strict+footnotes+definition_lists\f[] is +strict markdown with footnotes and definition lists enabled, and +\f[C]markdown\-pipe_tables+hard_line_breaks\f[] is pandoc\[aq]s markdown +without pipe tables and with hard line breaks. +See Pandoc\[aq]s markdown, below, for a list of extensions and their +names. .RS .RE .TP -.B \f[C]-t\f[] \f[I]FORMAT\f[], \f[C]-w\f[] \f[I]FORMAT\f[], -\f[C]--to=\f[]\f[I]FORMAT\f[], \f[C]--write=\f[]\f[I]FORMAT\f[] +.B \f[C]\-t\f[] \f[I]FORMAT\f[], \f[C]\-w\f[] \f[I]FORMAT\f[], +\f[C]\-\-to=\f[]\f[I]FORMAT\f[], \f[C]\-\-write=\f[]\f[I]FORMAT\f[] Specify output format. \f[I]FORMAT\f[] can be \f[C]native\f[] (native Haskell), \f[C]json\f[] (JSON version of native AST), \f[C]plain\f[] (plain text), -\f[C]markdown\f[] (markdown), \f[C]rst\f[] (reStructuredText), -\f[C]html\f[] (XHTML 1), \f[C]html5\f[] (HTML 5), \f[C]latex\f[] -(LaTeX), \f[C]beamer\f[] (LaTeX beamer slide show), \f[C]context\f[] -(ConTeXt), \f[C]man\f[] (groff man), \f[C]mediawiki\f[] (MediaWiki -markup), \f[C]textile\f[] (Textile), \f[C]org\f[] (Emacs Org-Mode), -\f[C]texinfo\f[] (GNU Texinfo), \f[C]docbook\f[] (DocBook XML), -\f[C]opendocument\f[] (OpenDocument XML), \f[C]odt\f[] (OpenOffice text -document), \f[C]docx\f[] (Word docx), \f[C]epub\f[] (EPUB book), +\f[C]markdown\f[] (pandoc\[aq]s extended markdown), +\f[C]markdown_strict\f[] (original unextended markdown), +\f[C]markdown_phpextra\f[] (PHP Markdown extra extended markdown), +\f[C]markdown_github\f[] (github extended markdown), \f[C]rst\f[] +(reStructuredText), \f[C]html\f[] (XHTML 1), \f[C]html5\f[] (HTML 5), +\f[C]latex\f[] (LaTeX), \f[C]beamer\f[] (LaTeX beamer slide show), +\f[C]context\f[] (ConTeXt), \f[C]man\f[] (groff man), \f[C]mediawiki\f[] +(MediaWiki markup), \f[C]textile\f[] (Textile), \f[C]org\f[] (Emacs +Org\-Mode), \f[C]texinfo\f[] (GNU Texinfo), \f[C]docbook\f[] (DocBook +XML), \f[C]opendocument\f[] (OpenDocument XML), \f[C]odt\f[] (OpenOffice +text document), \f[C]docx\f[] (Word docx), \f[C]epub\f[] (EPUB book), +\f[C]epub3\f[] (EPUB v3), \f[C]fb2\f[] (FictionBook2 e\-book), \f[C]asciidoc\f[] (AsciiDoc), \f[C]slidy\f[] (Slidy HTML and javascript slide show), \f[C]slideous\f[] (Slideous HTML and javascript slide show), \f[C]dzslides\f[] (HTML5 + javascript slide show), \f[C]s5\f[] (S5 HTML and javascript slide show), or \f[C]rtf\f[] (rich text format). -Note that \f[C]odt\f[] and \f[C]epub\f[] output will not be directed to -\f[I]stdout\f[]; an output filename must be specified using the -\f[C]-o/--output\f[] option. +Note that \f[C]odt\f[], \f[C]epub\f[], and \f[C]epub3\f[] output will +not be directed to \f[I]stdout\f[]; an output filename must be specified +using the \f[C]\-o/\-\-output\f[] option. If \f[C]+lhs\f[] is appended to \f[C]markdown\f[], \f[C]rst\f[], \f[C]latex\f[], \f[C]beamer\f[], \f[C]html\f[], or \f[C]html5\f[], the output will be rendered as literate Haskell source: see Literate Haskell support, below. +Markdown syntax extensions can be individually enabled or disabled by +appending \f[C]+EXTENSION\f[] or \f[C]\-EXTENSION\f[] to the format +name, as described above under \f[C]\-f\f[]. .RS .RE .TP -.B \f[C]-o\f[] \f[I]FILE\f[], \f[C]--output=\f[]\f[I]FILE\f[] +.B \f[C]\-o\f[] \f[I]FILE\f[], \f[C]\-\-output=\f[]\f[I]FILE\f[] Write output to \f[I]FILE\f[] instead of \f[I]stdout\f[]. -If \f[I]FILE\f[] is \f[C]-\f[], output will go to \f[I]stdout\f[]. -(Exception: if the output format is \f[C]odt\f[], \f[C]docx\f[], or -\f[C]epub\f[], output to stdout is disabled.) +If \f[I]FILE\f[] is \f[C]\-\f[], output will go to \f[I]stdout\f[]. +(Exception: if the output format is \f[C]odt\f[], \f[C]docx\f[], +\f[C]epub\f[], or \f[C]epub3\f[], output to stdout is disabled.) .RS .RE .TP -.B \f[C]--data-dir=\f[]\f[I]DIRECTORY\f[] +.B \f[C]\-\-data\-dir=\f[]\f[I]DIRECTORY\f[] 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: @@ -235,26 +255,18 @@ A \f[C]reference.odt\f[], \f[C]reference.docx\f[], \f[C]default.csl\f[], will override pandoc\[aq]s normal defaults. .RE .TP -.B \f[C]-v\f[], \f[C]--version\f[] +.B \f[C]\-v\f[], \f[C]\-\-version\f[] Print version. .RS .RE .TP -.B \f[C]-h\f[], \f[C]--help\f[] +.B \f[C]\-h\f[], \f[C]\-\-help\f[] Show usage message. .RS .RE .SS Reader options .TP -.B \f[C]--strict\f[] -Use strict markdown syntax, with no pandoc extensions or variants. -When the input format is HTML, this means that constructs that have no -equivalents in standard markdown (e.g. -definition lists or strikeout text) will be parsed as raw HTML. -.RS -.RE -.TP -.B \f[C]-R\f[], \f[C]--parse-raw\f[] +.B \f[C]\-R\f[], \f[C]\-\-parse\-raw\f[] Parse untranslatable HTML codes and LaTeX environments as raw HTML or LaTeX, instead of ignoring them. Affects only HTML and LaTeX input. @@ -264,88 +276,91 @@ reStructuredText, LaTeX, and ConTeXt output. The default is for the readers to omit untranslatable HTML codes and LaTeX environments. (The LaTeX reader does pass through untranslatable LaTeX -\f[I]commands\f[], even if \f[C]-R\f[] is not specified.) +\f[I]commands\f[], even if \f[C]\-R\f[] is not specified.) .RS .RE .TP -.B \f[C]-S\f[], \f[C]--smart\f[] +.B \f[C]\-S\f[], \f[C]\-\-smart\f[] Produce typographically correct output, converting straight quotes to -curly quotes, \f[C]---\f[] to em-dashes, \f[C]--\f[] to en-dashes, and -\f[C]\&...\f[] to ellipses. +curly quotes, \f[C]\-\-\-\f[] to em\-dashes, \f[C]\-\-\f[] to +en\-dashes, and \f[C]\&...\f[] to ellipses. Nonbreaking spaces are inserted after certain abbreviations, such as "Mr." (Note: This option is significant only when the input format is -\f[C]markdown\f[] or \f[C]textile\f[]. +\f[C]markdown\f[], \f[C]markdown_strict\f[], or \f[C]textile\f[]. It is selected automatically when the input format is \f[C]textile\f[] or the output format is \f[C]latex\f[] or \f[C]context\f[], unless -\f[C]--no-tex-ligatures\f[] is used.) +\f[C]\-\-no\-tex\-ligatures\f[] is used.) .RS .RE .TP -.B \f[C]--old-dashes\f[] +.B \f[C]\-\-old\-dashes\f[] Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: -\f[C]-\f[] before a numeral is an en-dash, and \f[C]--\f[] is an -em-dash. +\f[C]\-\f[] before a numeral is an en\-dash, and \f[C]\-\-\f[] is an +em\-dash. This option is selected automatically for \f[C]textile\f[] input. .RS .RE .TP -.B \f[C]--base-header-level=\f[]\f[I]NUMBER\f[] +.B \f[C]\-\-base\-header\-level=\f[]\f[I]NUMBER\f[] Specify the base level for headers (defaults to 1). .RS .RE .TP -.B \f[C]--indented-code-classes=\f[]\f[I]CLASSES\f[] -Specify classes to use for indented code blocks--for example, +.B \f[C]\-\-indented\-code\-classes=\f[]\f[I]CLASSES\f[] +Specify classes to use for indented code blocks\-\-for example, \f[C]perl,numberLines\f[] or \f[C]haskell\f[]. Multiple classes may be separated by spaces or commas. .RS .RE .TP -.B \f[C]--normalize\f[] +.B \f[C]\-\-normalize\f[] Normalize the document after reading: merge adjacent \f[C]Str\f[] or \f[C]Emph\f[] elements, for example, and remove repeated \f[C]Space\f[]s. .RS .RE .TP -.B \f[C]-p\f[], \f[C]--preserve-tabs\f[] +.B \f[C]\-p\f[], \f[C]\-\-preserve\-tabs\f[] Preserve tabs instead of converting them to spaces (the default). +Note that this will only affect tabs in literal code spans and code +blocks; tabs in regular text will be treated as spaces. .RS .RE .TP -.B \f[C]--tab-stop=\f[]\f[I]NUMBER\f[] +.B \f[C]\-\-tab\-stop=\f[]\f[I]NUMBER\f[] Specify the number of spaces per tab (default is 4). .RS .RE .SS General writer options .TP -.B \f[C]-s\f[], \f[C]--standalone\f[] +.B \f[C]\-s\f[], \f[C]\-\-standalone\f[] Produce output with an appropriate header and footer (e.g. a standalone HTML, LaTeX, or RTF file, not a fragment). This option is set automatically for \f[C]pdf\f[], \f[C]epub\f[], -\f[C]docx\f[], and \f[C]odt\f[] output. +\f[C]epub3\f[], \f[C]fb2\f[], \f[C]docx\f[], and \f[C]odt\f[] output. .RS .RE .TP -.B \f[C]--template=\f[]\f[I]FILE\f[] +.B \f[C]\-\-template=\f[]\f[I]FILE\f[] Use \f[I]FILE\f[] as a custom template for the generated document. -Implies \f[C]--standalone\f[]. +Implies \f[C]\-\-standalone\f[]. See Templates below for a description of template syntax. If no extension is specified, an extension corresponding to the writer -will be added, so that \f[C]--template=special\f[] looks for +will be added, so that \f[C]\-\-template=special\f[] looks for \f[C]special.html\f[] for HTML output. If the template is not found, pandoc will search for it in the user data -directory (see \f[C]--data-dir\f[]). +directory (see \f[C]\-\-data\-dir\f[]). If this option is not used, a default template appropriate for the -output format will be used (see \f[C]-D/--print-default-template\f[]). +output format will be used (see +\f[C]\-D/\-\-print\-default\-template\f[]). .RS .RE .TP -.B \f[C]-V\f[] \f[I]KEY[=VAL]\f[], -\f[C]--variable=\f[]\f[I]KEY[:VAL]\f[] +.B \f[C]\-V\f[] \f[I]KEY[=VAL]\f[], +\f[C]\-\-variable=\f[]\f[I]KEY[:VAL]\f[] Set the template variable \f[I]KEY\f[] to the value \f[I]VAL\f[] when rendering the document in standalone mode. -This is generally only useful when the \f[C]--template\f[] option is +This is generally only useful when the \f[C]\-\-template\f[] option is used to specify a custom template, since pandoc automatically sets the variables used in the default templates. If no \f[I]VAL\f[] is specified, the key will be given the value @@ -353,25 +368,25 @@ If no \f[I]VAL\f[] is specified, the key will be given the value .RS .RE .TP -.B \f[C]-D\f[] \f[I]FORMAT\f[], -\f[C]--print-default-template=\f[]\f[I]FORMAT\f[] +.B \f[C]\-D\f[] \f[I]FORMAT\f[], +\f[C]\-\-print\-default\-template=\f[]\f[I]FORMAT\f[] Print the default template for an output \f[I]FORMAT\f[]. -(See \f[C]-t\f[] for a list of possible \f[I]FORMAT\f[]s.) +(See \f[C]\-t\f[] for a list of possible \f[I]FORMAT\f[]s.) .RS .RE .TP -.B \f[C]--no-wrap\f[] +.B \f[C]\-\-no\-wrap\f[] Disable text wrapping in output. By default, text is wrapped appropriately for the output format. .RS .RE .TP -.B \f[C]--columns\f[]=\f[I]NUMBER\f[] +.B \f[C]\-\-columns\f[]=\f[I]NUMBER\f[] Specify length of lines in characters (for text wrapping). .RS .RE .TP -.B \f[C]--toc\f[], \f[C]--table-of-contents\f[] +.B \f[C]\-\-toc\f[], \f[C]\-\-table\-of\-contents\f[] Include an automatically generated table of contents (or, in the case of \f[C]latex\f[], \f[C]context\f[], and \f[C]rst\f[], an instruction to create one) in the output document. @@ -380,13 +395,22 @@ This option has no effect on \f[C]man\f[], \f[C]docbook\f[], .RS .RE .TP -.B \f[C]--no-highlight\f[] +.B \f[C]\-\-toc\-depth=\f[]\f[I]NUMBER\f[] +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 headers will be +listed in the contents). +Implies \f[C]\-\-toc\f[]. +.RS +.RE +.TP +.B \f[C]\-\-no\-highlight\f[] Disables syntax highlighting for code blocks and inlines, even when a language attribute is given. .RS .RE .TP -.B \f[C]--highlight-style\f[]=\f[I]STYLE\f[] +.B \f[C]\-\-highlight\-style\f[]=\f[I]STYLE\f[] Specifies the coloring style to be used in highlighted source code. Options are \f[C]pygments\f[] (the default), \f[C]kate\f[], \f[C]monochrome\f[], \f[C]espresso\f[], \f[C]zenburn\f[], @@ -394,19 +418,20 @@ Options are \f[C]pygments\f[] (the default), \f[C]kate\f[], .RS .RE .TP -.B \f[C]-H\f[] \f[I]FILE\f[], \f[C]--include-in-header=\f[]\f[I]FILE\f[] +.B \f[C]\-H\f[] \f[I]FILE\f[], +\f[C]\-\-include\-in\-header=\f[]\f[I]FILE\f[] Include contents of \f[I]FILE\f[], 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[C]--standalone\f[]. +Implies \f[C]\-\-standalone\f[]. .RS .RE .TP -.B \f[C]-B\f[] \f[I]FILE\f[], -\f[C]--include-before-body=\f[]\f[I]FILE\f[] +.B \f[C]\-B\f[] \f[I]FILE\f[], +\f[C]\-\-include\-before\-body=\f[]\f[I]FILE\f[] Include contents of \f[I]FILE\f[], verbatim, at the beginning of the document body (e.g. after the \f[C]<body>\f[] tag in HTML, or the \f[C]\\begin{document}\f[] @@ -415,27 +440,27 @@ 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[C]--standalone\f[]. +Implies \f[C]\-\-standalone\f[]. .RS .RE .TP -.B \f[C]-A\f[] \f[I]FILE\f[], -\f[C]--include-after-body=\f[]\f[I]FILE\f[] +.B \f[C]\-A\f[] \f[I]FILE\f[], +\f[C]\-\-include\-after\-body=\f[]\f[I]FILE\f[] Include contents of \f[I]FILE\f[], verbatim, at the end of the document body (before the \f[C]</body>\f[] tag in HTML, or the \f[C]\\end{document}\f[] command in LaTeX). This option can be be used repeatedly to include multiple files. They will be included in the order specified. -Implies \f[C]--standalone\f[]. +Implies \f[C]\-\-standalone\f[]. .RS .RE .SS Options affecting specific writers .TP -.B \f[C]--self-contained\f[] +.B \f[C]\-\-self\-contained\f[] Produce a standalone HTML file with no external dependencies, using \f[C]data:\f[] URIs to incorporate the contents of linked scripts, stylesheets, images, and videos. -The resulting file should be "self-contained," in the sense that it +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 @@ -444,87 +469,92 @@ This option works only with HTML output formats, including Scripts, images, and stylesheets at absolute URLs will be downloaded; those at relative URLs will be sought first relative to the working directory, then relative to the user data directory (see -\f[C]--data-dir\f[]), and finally relative to pandoc\[aq]s default data -directory. +\f[C]\-\-data\-dir\f[]), and finally relative to pandoc\[aq]s default +data directory. .RS .RE .TP -.B \f[C]--offline\f[] -Deprecated synonym for \f[C]--self-contained\f[]. +.B \f[C]\-\-offline\f[] +Deprecated synonym for \f[C]\-\-self\-contained\f[]. .RS .RE .TP -.B \f[C]-5\f[], \f[C]--html5\f[] +.B \f[C]\-5\f[], \f[C]\-\-html5\f[] Produce HTML5 instead of HTML4. This option has no effect for writers other than \f[C]html\f[]. (\f[I]Deprecated:\f[] Use the \f[C]html5\f[] output format instead.) .RS .RE .TP -.B \f[C]--ascii\f[] +.B \f[C]\-\-html\-q\-tags\f[] +Use \f[C]<q>\f[] tags for quotes in HTML. +.RS +.RE +.TP +.B \f[C]\-\-ascii\f[] Use only ascii characters in output. Currently supported only for HTML output (which uses numerical entities -instead of UTF-8 when this option is selected). +instead of UTF\-8 when this option is selected). .RS .RE .TP -.B \f[C]--reference-links\f[] -Use reference-style links, rather than inline links, in writing markdown -or reStructuredText. +.B \f[C]\-\-reference\-links\f[] +Use reference\-style links, rather than inline links, in writing +markdown or reStructuredText. By default inline links are used. .RS .RE .TP -.B \f[C]--atx-headers\f[] +.B \f[C]\-\-atx\-headers\f[] Use ATX style headers in markdown output. -The default is to use setext-style headers for levels 1-2, and then ATX -headers. +The default is to use setext\-style headers for levels 1\-2, and then +ATX headers. .RS .RE .TP -.B \f[C]--chapters\f[] -Treat top-level headers as chapters in LaTeX, ConTeXt, and DocBook +.B \f[C]\-\-chapters\f[] +Treat top\-level headers as chapters in LaTeX, ConTeXt, and DocBook output. When the LaTeX template uses the report, book, or memoir class, this option is implied. -If \f[C]--beamer\f[] is used, top-level headers will become +If \f[C]\-\-beamer\f[] is used, top\-level headers will become \f[C]\\part{..}\f[]. .RS .RE .TP -.B \f[C]-N\f[], \f[C]--number-sections\f[] +.B \f[C]\-N\f[], \f[C]\-\-number\-sections\f[] Number section headings in LaTeX, ConTeXt, or HTML output. By default, sections are not numbered. .RS .RE .TP -.B \f[C]--no-tex-ligatures\f[] +.B \f[C]\-\-no\-tex\-ligatures\f[] Do not convert quotation marks, apostrophes, and dashes to the TeX ligatures when writing LaTeX or ConTeXt. Instead, just use literal unicode characters. This is needed for using advanced OpenType features with XeLaTeX and LuaLaTeX. -Note: normally \f[C]--smart\f[] is selected automatically for LaTeX and -ConTeXt output, but it must be specified explicitly if -\f[C]--no-tex-ligatures\f[] is selected. +Note: normally \f[C]\-\-smart\f[] is selected automatically for LaTeX +and ConTeXt output, but it must be specified explicitly if +\f[C]\-\-no\-tex\-ligatures\f[] is selected. If you use literal curly quotes, dashes, and ellipses in your source, -then you may want to use \f[C]--no-tex-ligatures\f[] without -\f[C]--smart\f[]. +then you may want to use \f[C]\-\-no\-tex\-ligatures\f[] without +\f[C]\-\-smart\f[]. .RS .RE .TP -.B \f[C]--listings\f[] +.B \f[C]\-\-listings\f[] Use listings package for LaTeX code blocks .RS .RE .TP -.B \f[C]-i\f[], \f[C]--incremental\f[] +.B \f[C]\-i\f[], \f[C]\-\-incremental\f[] Make list items in slide shows display incrementally (one by one). The default is for lists to be displayed all at once. .RS .RE .TP -.B \f[C]--slide-level\f[]=\f[I]NUMBER\f[] +.B \f[C]\-\-slide\-level\f[]=\f[I]NUMBER\f[] Specifies that headers with the specified level create slides (for \f[C]beamer\f[], \f[C]s5\f[], \f[C]slidy\f[], \f[C]slideous\f[], \f[C]dzslides\f[]). @@ -536,7 +566,7 @@ document; see Structuring the slide show, below. .RS .RE .TP -.B \f[C]--section-divs\f[] +.B \f[C]\-\-section\-divs\f[] Wrap sections in \f[C]<div>\f[] tags (or \f[C]<section>\f[] tags in HTML5), and attach identifiers to the enclosing \f[C]<div>\f[] (or \f[C]<section>\f[]) rather than the header itself. @@ -544,40 +574,41 @@ See Section identifiers, below. .RS .RE .TP -.B \f[C]--email-obfuscation=\f[]\f[I]none|javascript|references\f[] +.B \f[C]\-\-email\-obfuscation=\f[]\f[I]none|javascript|references\f[] Specify a method for obfuscating \f[C]mailto:\f[] links in HTML documents. \f[I]none\f[] leaves \f[C]mailto:\f[] links as they are. \f[I]javascript\f[] obfuscates them using javascript. \f[I]references\f[] obfuscates them by printing their letters as decimal or hexadecimal character references. -If \f[C]--strict\f[] is specified, \f[I]references\f[] is used -regardless of the presence of this option. .RS .RE .TP -.B \f[C]--id-prefix\f[]=\f[I]STRING\f[] +.B \f[C]\-\-id\-prefix\f[]=\f[I]STRING\f[] Specify a prefix to be added to all automatically generated identifiers -in HTML output. +in HTML and DocBook output, and to footnote numbers in markdown output. This is useful for preventing duplicate identifiers when generating fragments to be included in other pages. .RS .RE .TP -.B \f[C]-T\f[] \f[I]STRING\f[], \f[C]--title-prefix=\f[]\f[I]STRING\f[] +.B \f[C]\-T\f[] \f[I]STRING\f[], +\f[C]\-\-title\-prefix=\f[]\f[I]STRING\f[] Specify \f[I]STRING\f[] 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[C]--standalone\f[]. +Implies \f[C]\-\-standalone\f[]. .RS .RE .TP -.B \f[C]-c\f[] \f[I]URL\f[], \f[C]--css=\f[]\f[I]URL\f[] +.B \f[C]\-c\f[] \f[I]URL\f[], \f[C]\-\-css=\f[]\f[I]URL\f[] Link to a CSS style sheet. +This option can be be used repeatedly to include multiple files. +They will be included in the order specified. .RS .RE .TP -.B \f[C]--reference-odt=\f[]\f[I]FILE\f[] +.B \f[C]\-\-reference\-odt=\f[]\f[I]FILE\f[] Use the specified file as a style reference in producing an ODT. For best results, the reference ODT should be a modified version of an ODT produced using pandoc. @@ -585,12 +616,12 @@ 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[C]reference.odt\f[] in the user data directory (see -\f[C]--data-dir\f[]). +\f[C]\-\-data\-dir\f[]). If this is not found either, sensible defaults will be used. .RS .RE .TP -.B \f[C]--reference-docx=\f[]\f[I]FILE\f[] +.B \f[C]\-\-reference\-docx=\f[]\f[I]FILE\f[] Use the specified file as a style reference in producing a docx file. For best results, the reference docx should be a modified version of a docx file produced using pandoc. @@ -598,37 +629,43 @@ The contents of the reference docx are ignored, but its stylesheets are used in the new docx. If no reference docx is specified on the command line, pandoc will look for a file \f[C]reference.docx\f[] in the user data directory (see -\f[C]--data-dir\f[]). +\f[C]\-\-data\-dir\f[]). If this is not found either, sensible defaults will be used. +The following styles are used by pandoc: [paragraph] Normal, Title, +Authors, Date, Heading 1, Heading 2, Heading 3, Heading 4, Heading 5, +Block Quote, Definition Term, Definition, Body Text, Table Caption, +Image Caption; [character] Default Paragraph Font, Body Text Char, +Verbatim Char, Footnote Reference, Hyperlink. .RS .RE .TP -.B \f[C]--epub-stylesheet=\f[]\f[I]FILE\f[] +.B \f[C]\-\-epub\-stylesheet=\f[]\f[I]FILE\f[] Use the specified CSS file to style the EPUB. If no stylesheet is specified, pandoc will look for a file -\f[C]epub.css\f[] in the user data directory (see \f[C]--data-dir\f[]). +\f[C]epub.css\f[] in the user data directory (see +\f[C]\-\-data\-dir\f[]). If it is not found there, sensible defaults will be used. .RS .RE .TP -.B \f[C]--epub-cover-image=\f[]\f[I]FILE\f[] +.B \f[C]\-\-epub\-cover\-image=\f[]\f[I]FILE\f[] Use the specified image as the EPUB cover. It is recommended that the image be less than 1000px in width and height. .RS .RE .TP -.B \f[C]--epub-metadata=\f[]\f[I]FILE\f[] +.B \f[C]\-\-epub\-metadata=\f[]\f[I]FILE\f[] Look in the specified XML file for metadata for the EPUB. The file should contain a series of Dublin Core elements, as documented -at \f[C]http://dublincore.org/documents/dces/\f[]. +at http://dublincore.org/documents/dces/. For example: .RS .IP .nf \f[C] \ <dc:rights>Creative\ Commons</dc:rights> -\ <dc:language>es-AR</dc:language> +\ <dc:language>es\-AR</dc:language> \f[] .fi .PP @@ -641,67 +678,81 @@ which should be in ISO 8601 format), \f[C]<dc:language>\f[] (from the Any of these may be overridden by elements in the metadata file. .RE .TP -.B \f[C]--epub-embed-font=\f[]\f[I]FILE\f[] +.B \f[C]\-\-epub\-embed\-font=\f[]\f[I]FILE\f[] Embed the specified font in the EPUB. This option can be repeated to embed multiple fonts. To use embedded fonts, you will need to add declarations like the -following to your CSS (see \f[C]--epub-stylesheet\f[]): +following to your CSS (see \f[C]\-\-epub\-stylesheet\f[]): .RS .IP .nf \f[C] -\@font-face\ { -font-family:\ DejaVuSans; -font-style:\ normal; -font-weight:\ normal; -src:url("DejaVuSans-Regular.ttf"); +\@font\-face\ { +font\-family:\ DejaVuSans; +font\-style:\ normal; +font\-weight:\ normal; +src:url("DejaVuSans\-Regular.ttf"); } -\@font-face\ { -font-family:\ DejaVuSans; -font-style:\ normal; -font-weight:\ bold; -src:url("DejaVuSans-Bold.ttf"); +\@font\-face\ { +font\-family:\ DejaVuSans; +font\-style:\ normal; +font\-weight:\ bold; +src:url("DejaVuSans\-Bold.ttf"); } -\@font-face\ { -font-family:\ DejaVuSans; -font-style:\ italic; -font-weight:\ normal; -src:url("DejaVuSans-Oblique.ttf"); +\@font\-face\ { +font\-family:\ DejaVuSans; +font\-style:\ italic; +font\-weight:\ normal; +src:url("DejaVuSans\-Oblique.ttf"); } -\@font-face\ { -font-family:\ DejaVuSans; -font-style:\ italic; -font-weight:\ bold; -src:url("DejaVuSans-BoldOblique.ttf"); +\@font\-face\ { +font\-family:\ DejaVuSans; +font\-style:\ italic; +font\-weight:\ bold; +src:url("DejaVuSans\-BoldOblique.ttf"); } -body\ {\ font-family:\ "DejaVuSans";\ } +body\ {\ font\-family:\ "DejaVuSans";\ } \f[] .fi .RE .TP -.B \f[C]--latex-engine=\f[]\f[I]pdflatex|lualatex|xelatex\f[] +.B \f[C]\-\-epub\-chapter\-level=\f[]\f[I]NUMBER\f[] +Specify the header level at which to split the EPUB into separate +"chapter" files. +The default is to split into chapters at level 1 headers. +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 headers, one might want to use a +chapter level of 2 or 3. +.RS +.RE +.TP +.B \f[C]\-\-latex\-engine=\f[]\f[I]pdflatex|lualatex|xelatex\f[] Use the specified LaTeX engine when producing PDF output. The default is \f[C]pdflatex\f[]. If the engine is not in your PATH, the full path of the engine may be specified here. .RS .RE -.SS Citations +.SS Citation rendering .TP -.B \f[C]--bibliography=\f[]\f[I]FILE\f[] +.B \f[C]\-\-bibliography=\f[]\f[I]FILE\f[] Specify bibliography database to be used in resolving citations. The database type will be determined from the extension of \f[I]FILE\f[], which may be \f[C]\&.mods\f[] (MODS format), -\f[C]\&.bib\f[] (BibTeX/BibLaTeX format), \f[C]\&.ris\f[] (RIS format), -\f[C]\&.enl\f[] (EndNote format), \f[C]\&.xml\f[] (EndNote XML format), -\f[C]\&.wos\f[] (ISI format), \f[C]\&.medline\f[] (MEDLINE format), -\f[C]\&.copac\f[] (Copac format), or \f[C]\&.json\f[] (citeproc JSON). +\f[C]\&.bib\f[] (BibLaTeX format, which will normally work for BibTeX +files as well), \f[C]\&.bibtex\f[] (BibTeX format), \f[C]\&.ris\f[] (RIS +format), \f[C]\&.enl\f[] (EndNote format), \f[C]\&.xml\f[] (EndNote XML +format), \f[C]\&.wos\f[] (ISI format), \f[C]\&.medline\f[] (MEDLINE +format), \f[C]\&.copac\f[] (Copac format), or \f[C]\&.json\f[] (citeproc +JSON). If you want to use multiple bibliographies, just use this option repeatedly. .RS .RE .TP -.B \f[C]--csl=\f[]\f[I]FILE\f[] +.B \f[C]\-\-csl=\f[]\f[I]FILE\f[] Specify CSL style to be used in formatting citations and the bibliography. If \f[I]FILE\f[] is not found, pandoc will look for it in @@ -722,25 +773,25 @@ C:\\Documents\ And\ Settings\\USERNAME\\Application\ Data\\csl .fi .PP in Windows. -If the \f[C]--csl\f[] option is not specified, pandoc will use a default -style: either \f[C]default.csl\f[] in the user data directory (see -\f[C]--data-dir\f[]), or, if that is not present, the Chicago -author-date style. +If the \f[C]\-\-csl\f[] option is not specified, pandoc will use a +default style: either \f[C]default.csl\f[] in the user data directory +(see \f[C]\-\-data\-dir\f[]), or, if that is not present, the Chicago +author\-date style. .RE .TP -.B \f[C]--citation-abbreviations=\f[]\f[I]FILE\f[] +.B \f[C]\-\-citation\-abbreviations=\f[]\f[I]FILE\f[] Specify a file containing abbreviations for journal titles and other bibliographic fields (indicated by setting \f[C]form="short"\f[] in the CSL node for the field). The format is described at -\f[C]http://citationstylist.org/2011/10/19/abbreviations-for-zotero-test-release/\f[]. +http://citationstylist.org/2011/10/19/abbreviations\-for\-zotero\-test\-release/. Here is a short example: .RS .IP .nf \f[C] {\ "default":\ { -\ \ \ \ "container-title":\ { +\ \ \ \ "container\-title":\ { \ \ \ \ \ \ \ \ \ \ \ \ "Lloyd\[aq]s\ Law\ Reports":\ "Lloyd\[aq]s\ Rep", \ \ \ \ \ \ \ \ \ \ \ \ "Estates\ Gazette":\ "EG", \ \ \ \ \ \ \ \ \ \ \ \ "Scots\ Law\ Times":\ "SLT" @@ -751,18 +802,18 @@ Here is a short example: .fi .RE .TP -.B \f[C]--natbib\f[] +.B \f[C]\-\-natbib\f[] Use natbib for citations in LaTeX output. .RS .RE .TP -.B \f[C]--biblatex\f[] +.B \f[C]\-\-biblatex\f[] Use biblatex for citations in LaTeX output. .RS .RE .SS Math rendering in HTML .TP -.B \f[C]-m\f[] [\f[I]URL\f[]], \f[C]--latexmathml\f[][=\f[I]URL\f[]] +.B \f[C]\-m\f[] [\f[I]URL\f[]], \f[C]\-\-latexmathml\f[][=\f[I]URL\f[]] Use the LaTeXMathML script to display embedded TeX math in HTML output. To insert a link to a local copy of the \f[C]LaTeXMathML.js\f[] script, provide a \f[I]URL\f[]. @@ -774,7 +825,7 @@ copy of the script, so it can be cached. .RS .RE .TP -.B \f[C]--mathml\f[][=\f[I]URL\f[]] +.B \f[C]\-\-mathml\f[][=\f[I]URL\f[]] Convert TeX math to MathML (in \f[C]docbook\f[] as well as \f[C]html\f[] and \f[C]html5\f[]). In standalone \f[C]html\f[] output, a small javascript (or a link to @@ -783,7 +834,7 @@ allows the MathML to be viewed on some browsers. .RS .RE .TP -.B \f[C]--jsmath\f[][=\f[I]URL\f[]] +.B \f[C]\-\-jsmath\f[][=\f[I]URL\f[]] Use jsMath to display embedded TeX math in HTML output. The \f[I]URL\f[] should point to the jsMath load script (e.g. \f[C]jsMath/easy/load.js\f[]); if provided, it will be linked to in the @@ -794,7 +845,7 @@ the HTML template. .RS .RE .TP -.B \f[C]--mathjax\f[][=\f[I]URL\f[]] +.B \f[C]\-\-mathjax\f[][=\f[I]URL\f[]] Use MathJax to display embedded TeX math in HTML output. The \f[I]URL\f[] should point to the \f[C]MathJax.js\f[] load script. If a \f[I]URL\f[] is not provided, a link to the MathJax CDN will be @@ -802,21 +853,21 @@ inserted. .RS .RE .TP -.B \f[C]--gladtex\f[] +.B \f[C]\-\-gladtex\f[] Enclose TeX math in \f[C]<eq>\f[] tags in HTML output. These can then be processed by gladTeX to produce links to images of the typeset formulas. .RS .RE .TP -.B \f[C]--mimetex\f[][=\f[I]URL\f[]] +.B \f[C]\-\-mimetex\f[][=\f[I]URL\f[]] Render TeX math using the mimeTeX CGI script. If \f[I]URL\f[] is not specified, it is assumed that the script is at -\f[C]/cgi-bin/mimetex.cgi\f[]. +\f[C]/cgi\-bin/mimetex.cgi\f[]. .RS .RE .TP -.B \f[C]--webtex\f[][=\f[I]URL\f[]] +.B \f[C]\-\-webtex\f[][=\f[I]URL\f[]] Render TeX formulas using an external script that converts TeX formulas to images. The formula will be concatenated with the URL provided. @@ -825,30 +876,30 @@ If \f[I]URL\f[] is not specified, the Google Chart API will be used. .RE .SS Options for wrapper scripts .TP -.B \f[C]--dump-args\f[] -Print information about command-line arguments to \f[I]stdout\f[], then +.B \f[C]\-\-dump\-args\f[] +Print information about command\-line arguments to \f[I]stdout\f[], 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[C]-o\f[] option, or \f[C]-\f[] (for \f[I]stdout\f[]) if no +with the \f[C]\-o\f[] option, or \f[C]\-\f[] (for \f[I]stdout\f[]) if no output file was specified. -The remaining lines contain the command-line arguments, one per line, in -the order they appear. +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[C]--\f[] separator at the end +include any options appearing after a \f[C]\-\-\f[] separator at the end of the line. .RS .RE .TP -.B \f[C]--ignore-args\f[] -Ignore command-line arguments (for use in wrapper scripts). +.B \f[C]\-\-ignore\-args\f[] +Ignore command\-line arguments (for use in wrapper scripts). Regular Pandoc options are not ignored. Thus, for example, .RS .IP .nf \f[C] -pandoc\ --ignore-args\ -o\ foo.html\ -s\ foo.txt\ --\ -e\ latin1 +pandoc\ \-\-ignore\-args\ \-o\ foo.html\ \-s\ foo.txt\ \-\-\ \-e\ latin1 \f[] .fi .PP @@ -856,38 +907,36 @@ is equivalent to .IP .nf \f[C] -pandoc\ -o\ foo.html\ -s +pandoc\ \-o\ foo.html\ \-s \f[] .fi .RE .SH TEMPLATES .PP -When the \f[C]-s/--standalone\f[] option is used, pandoc uses a template -to add header and footer material that is needed for a self-standing -document. +When the \f[C]\-s/\-\-standalone\f[] 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 .nf \f[C] -pandoc\ -D\ FORMAT +pandoc\ \-D\ FORMAT \f[] .fi .PP where \f[C]FORMAT\f[] is the name of the output format. -A custom template can be specified using the \f[C]--template\f[] option. +A custom template can be specified using the \f[C]\-\-template\f[] +option. You can also override the system default templates for a given output format \f[C]FORMAT\f[] by putting a file \f[C]templates/default.FORMAT\f[] in the user data directory (see -\f[C]--data-dir\f[], above). +\f[C]\-\-data\-dir\f[], above). \f[I]Exceptions:\f[] For \f[C]odt\f[] output, customize the \f[C]default.opendocument\f[] template. For \f[C]pdf\f[] output, customize the \f[C]default.latex\f[] template. -For \f[C]epub\f[] output, customize the \f[C]epub-page.html\f[], -\f[C]epub-coverimage.html\f[], and \f[C]epub-titlepage.html\f[] -templates. .PP Templates may contain \f[I]variables\f[]. -Variable names are sequences of alphanumerics, \f[C]-\f[], and +Variable names are sequences of alphanumerics, \f[C]\-\f[], and \f[C]_\f[], starting with a letter. A variable name surrounded by \f[C]$\f[] signs will be replaced by its value. @@ -906,25 +955,26 @@ To write a literal \f[C]$\f[] in a template, use \f[C]$$\f[]. Some variables are set automatically by pandoc. These vary somewhat depending on the output format, but include: .TP -.B \f[C]header-includes\f[] -contents specified by \f[C]-H/--include-in-header\f[] (may have multiple -values) +.B \f[C]header\-includes\f[] +contents specified by \f[C]\-H/\-\-include\-in\-header\f[] (may have +multiple values) .RS .RE .TP .B \f[C]toc\f[] -non-null value if \f[C]--toc/--table-of-contents\f[] was specified +non\-null value if \f[C]\-\-toc/\-\-table\-of\-contents\f[] was +specified .RS .RE .TP -.B \f[C]include-before\f[] -contents specified by \f[C]-B/--include-before-body\f[] (may have +.B \f[C]include\-before\f[] +contents specified by \f[C]\-B/\-\-include\-before\-body\f[] (may have multiple values) .RS .RE .TP -.B \f[C]include-after\f[] -contents specified by \f[C]-A/--include-after-body\f[] (may have +.B \f[C]include\-after\f[] +contents specified by \f[C]\-A/\-\-include\-after\-body\f[] (may have multiple values) .RS .RE @@ -955,18 +1005,18 @@ language code for HTML or LaTeX documents .RS .RE .TP -.B \f[C]slidy-url\f[] +.B \f[C]slidy\-url\f[] base URL for Slidy documents (defaults to \f[C]http://www.w3.org/Talks/Tools/Slidy2\f[]) .RS .RE .TP -.B \f[C]slideous-url\f[] +.B \f[C]slideous\-url\f[] base URL for Slideous documents (defaults to \f[C]default\f[]) .RS .RE .TP -.B \f[C]s5-url\f[] +.B \f[C]s5\-url\f[] base URL for S5 documents (defaults to \f[C]ui/default\f[]) .RS .RE @@ -1015,13 +1065,13 @@ color for external links in LaTeX documents .RS .RE .TP -.B \f[C]links-as-notes\f[] +.B \f[C]links\-as\-notes\f[] causes links to be printed as footnotes in LaTeX documents .RS .RE .PP Variables may be set at the command line using the -\f[C]-V/--variable\f[] option. +\f[C]\-V/\-\-variable\f[] option. This allows users to include custom variables in their templates. .PP Templates may contain conditionals. @@ -1038,13 +1088,13 @@ $endif$ .fi .PP This will include \f[C]X\f[] in the template if \f[C]variable\f[] has a -non-null value; otherwise it will include \f[C]Y\f[]. +non\-null value; otherwise it will include \f[C]Y\f[]. \f[C]X\f[] and \f[C]Y\f[] are placeholders for any valid template text, and may include interpolated variables or other conditionals. The \f[C]$else$\f[] section may be omitted. .PP When variables can have multiple values (for example, \f[C]author\f[] in -a multi-author document), you can use the \f[C]$for$\f[] keyword: +a multi\-author document), you can use the \f[C]$for$\f[] keyword: .IP .nf \f[C] @@ -1067,9 +1117,113 @@ 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 -(\f[C]http://github.com/jgm/pandoc-templates\f[]) and merge in changes -after each pandoc release. +An easy way to do this is to fork the pandoc\-templates repository +(http://github.com/jgm/pandoc\-templates) and merge in changes after +each pandoc release. +.SH NON\-PANDOC EXTENSIONS +.PP +The following markdown syntax extensions are not enabled by default in +pandoc, but may be enabled by adding \f[C]+EXTENSION\f[] to the format +name, where \f[C]EXTENSION\f[] is the name of the extension. +Thus, for example, \f[C]markdown+hard_line_breaks\f[] is markdown with +hard line breaks. +.PP +\f[B]Extension: \f[C]hard_line_breaks\f[]\f[] +.PD 0 +.P +.PD +Causes all newlines within a paragraph to be interpreted as hard line +breaks instead of spaces. +.PP +\f[B]Extension: \f[C]tex_math_single_backslash\f[]\f[] +.PD 0 +.P +.PD +Causes anything between \f[C]\\(\f[] and \f[C]\\)\f[] to be interpreted +as inline TeX math, and anything between \f[C]\\[\f[] and \f[C]\\]\f[] +to be interpreted as display TeX math. +Note: a drawback of this extension is that it precludes escaping +\f[C](\f[] and \f[C][\f[]. +.PP +\f[B]Extension: \f[C]tex_math_double_backslash\f[]\f[] +.PD 0 +.P +.PD +Causes anything between \f[C]\\\\(\f[] and \f[C]\\\\)\f[] to be +interpreted as inline TeX math, and anything between \f[C]\\\\[\f[] and +\f[C]\\\\]\f[] to be interpreted as display TeX math. +.PP +\f[B]Extension: \f[C]markdown_attribute\f[]\f[] +.PD 0 +.P +.PD +By default, pandoc interprets material inside block\-level tags as +markdown. +This extension changes the behavior so that markdown is only parsed +inside block\-level tags if the tags have the attribute +\f[C]markdown=1\f[]. +.PP +\f[B]Extension: \f[C]mmd_title_block\f[]\f[] +.PD 0 +.P +.PD +Enables a MultiMarkdown style title block at the top of the document, +for example: +.IP +.nf +\f[C] +Title:\ \ \ My\ title +Author:\ \ John\ Doe +Date:\ \ \ \ September\ 1,\ 2008 +Comment:\ This\ is\ a\ sample\ mmd\ title\ block,\ with +\ \ \ \ \ \ \ \ \ a\ field\ spanning\ multiple\ lines. +\f[] +.fi +.PP +See the MultiMarkdown documentation for details. +Note that only title, author, and date are recognized; other fields are +simply ignored by pandoc. +If \f[C]pandoc_title_block\f[] is enabled, it will take precedence over +\f[C]mmd_title_block\f[]. +.PP +\f[B]Extension: \f[C]abbrevations\f[]\f[] +.PD 0 +.P +.PD +Parses PHP Markdown Extra abbreviation keys, like +.IP +.nf +\f[C] +*[HTML]:\ Hyper\ Text\ Markup\ Language +\f[] +.fi +.PP +Note that the pandoc document model does not support abbreviations, so +if this extension is enabled, abbreviation keys are simply skipped (as +opposed to being parsed as paragraphs). +.PP +\f[B]Extension: \f[C]autolink_bare_uris\f[]\f[] +.PD 0 +.P +.PD +Makes all absolute URIs into links, even when not surrounded by pointy +braces \f[C]<...>\f[]. +.PP +\f[B]Extension: \f[C]link_attributes\f[]\f[] +.PD 0 +.P +.PD +Parses multimarkdown style key\-value attributes on link and image +references. +Note that pandoc\[aq]s internal document model provides nowhere to put +these, so they are presently just ignored. +.PP +\f[B]Extension: \f[C]mmd_header_identifiers\f[]\f[] +.PD 0 +.P +.PD +Parses multimarkdown style header identifiers (in square brackets, after +the header but before any trailing \f[C]#\f[]s in an ATX header). .SH PRODUCING SLIDE SHOWS WITH PANDOC .PP You can use Pandoc to produce an HTML + javascript slide presentation @@ -1090,29 +1244,29 @@ Here\[aq]s the markdown source for a simple slide show, ##\ Getting\ up --\ Turn\ off\ alarm --\ Get\ out\ of\ bed +\-\ Turn\ off\ alarm +\-\ Get\ out\ of\ bed ##\ Breakfast --\ Eat\ eggs --\ Drink\ coffee +\-\ Eat\ eggs +\-\ Drink\ coffee #\ In\ the\ evening ##\ Dinner --\ Eat\ spaghetti --\ Drink\ wine +\-\ Eat\ spaghetti +\-\ Drink\ wine ------------------- +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- ![picture\ of\ spaghetti](images/spaghetti.jpg) ##\ Going\ to\ sleep --\ Get\ in\ bed --\ Count\ sheep +\-\ Get\ in\ bed +\-\ Count\ sheep \f[] .fi .PP @@ -1120,7 +1274,7 @@ To produce the slide show, simply type .IP .nf \f[C] -pandoc\ -t\ s5\ -s\ habits.txt\ -o\ habits.html +pandoc\ \-t\ s5\ \-s\ habits.txt\ \-o\ habits.html \f[] .fi .PP @@ -1128,7 +1282,7 @@ for S5, .IP .nf \f[C] -pandoc\ -t\ slidy\ -s\ habits.txt\ -o\ habits.html +pandoc\ \-t\ slidy\ \-s\ habits.txt\ \-o\ habits.html \f[] .fi .PP @@ -1136,7 +1290,7 @@ for Slidy, .IP .nf \f[C] -pandoc\ -t\ slideous\ -s\ habits.txt\ -o\ habits.html +pandoc\ \-t\ slideous\ \-s\ habits.txt\ \-o\ habits.html \f[] .fi .PP @@ -1144,7 +1298,7 @@ for Slideous, .IP .nf \f[C] -pandoc\ -t\ dzslides\ -s\ habits.txt\ -o\ habits.html +pandoc\ \-t\ dzslides\ \-s\ habits.txt\ \-o\ habits.html \f[] .fi .PP @@ -1152,16 +1306,16 @@ for DZSlides, or .IP .nf \f[C] -pandoc\ -t\ beamer\ habits.txt\ -o\ habits.pdf +pandoc\ \-t\ beamer\ habits.txt\ \-o\ habits.pdf \f[] .fi .PP for beamer. .PP -With all HTML slide formats, the \f[C]--self-contained\f[] option can be -used to produce a single file that contains all of the data necessary to -display the slide show, including linked scripts, stylesheets, images, -and videos. +With all HTML slide formats, the \f[C]\-\-self\-contained\f[] option can +be used to produce a single file that contains all of the data necessary +to display the slide show, including linked scripts, stylesheets, +images, and videos. .SS Structuring the slide show .PP By default, the \f[I]slide level\f[] is the highest header level in the @@ -1169,7 +1323,8 @@ hierarchy that is followed immediately by content, and not another header, somewhere in the document. In the example above, level 1 headers are always followed by level 2 headers, which are followed by content, so 2 is the slide level. -This default can be overridden using the \f[C]--slide-level\f[] option. +This default can be overridden using the \f[C]\-\-slide\-level\f[] +option. .PP The document is carved up into slides according to the following rules: .IP \[bu] 2 @@ -1192,33 +1347,31 @@ lines in the default template.) These rules are designed to support many different styles of slide show. If you don\[aq]t care about structuring your slides into sections and subsections, you can just use level 1 headers for all each slide. -(In that case, level 1 will be the slide level.) - But you can also structure the slide show into sections, as in the -example above. +(In that case, level 1 will be the slide level.) But you can also +structure the slide show into sections, as in the example above. .PP For Slidy, Slideous and S5, the file produced by pandoc with the -\f[C]-s/--standalone\f[] option embeds a link to javascripts and CSS +\f[C]\-s/\-\-standalone\f[] option embeds a link to javascripts and CSS files, which are assumed to be available at the relative path \f[C]s5/default\f[] (for S5) or \f[C]slideous\f[] (for Slideous), or at the Slidy website at \f[C]w3.org\f[] (for Slidy). -(These paths can be changed by setting the \f[C]slidy-url\f[], -\f[C]slideous-url\f[] or \f[C]s5-url\f[] variables; see -\f[C]--variable\f[], above.) - For DZSlides, the (relatively short) javascript and css are included in -the file by default. +(These paths can be changed by setting the \f[C]slidy\-url\f[], +\f[C]slideous\-url\f[] or \f[C]s5\-url\f[] variables; see +\f[C]\-\-variable\f[], above.) For DZSlides, the (relatively short) +javascript and css are included in the file by default. .SS Incremental lists .PP By default, these writers produces lists that display "all at once." If you want your lists to display incrementally (one item at a time), use -the \f[C]-i\f[] option. +the \f[C]\-i\f[] option. If you want a particular list to depart from the default (that is, to -display incrementally without the \f[C]-i\f[] option and all at once -with the \f[C]-i\f[] option), put it in a block quote: +display incrementally without the \f[C]\-i\f[] option and all at once +with the \f[C]\-i\f[] option), put it in a block quote: .IP .nf \f[C] ->\ -\ Eat\ spaghetti ->\ -\ Drink\ wine +>\ \-\ Eat\ spaghetti +>\ \-\ Drink\ wine \f[] .fi .PP @@ -1229,10 +1382,10 @@ single document. You can change the style of HTML slides by putting customized CSS files in \f[C]$DATADIR/s5/default\f[] (for S5), \f[C]$DATADIR/slidy\f[] (for Slidy), or \f[C]$DATADIR/slideous\f[] (for Slideous), where -\f[C]$DATADIR\f[] is the user data directory (see \f[C]--data-dir\f[], -above). +\f[C]$DATADIR\f[] is the user data directory (see +\f[C]\-\-data\-dir\f[], above). The originals may be found in pandoc\[aq]s system data directory -(generally \f[C]$CABALDIR/pandoc-VERSION/s5/default\f[]). +(generally \f[C]$CABALDIR/pandoc\-VERSION/s5/default\f[]). Pandoc will look there for any files it does not find in the user data directory. .PP @@ -1240,19 +1393,20 @@ For dzslides, the CSS is included in the HTML file itself, and may be modified there. .PP To style beamer slides, you can specify a beamer "theme" or "colortheme" -using the \f[C]-V\f[] option: +using the \f[C]\-V\f[] option: .IP .nf \f[C] -pandoc\ -t\ beamer\ habits.txt\ -V\ theme:Warsaw\ -o\ habits.pdf +pandoc\ \-t\ beamer\ habits.txt\ \-V\ theme:Warsaw\ \-o\ habits.pdf \f[] .fi .SH LITERATE HASKELL SUPPORT .PP -If you append \f[C]+lhs\f[] to an appropriate input or output format -(\f[C]markdown\f[], \f[C]rst\f[], or \f[C]latex\f[] for input or output; -\f[C]beamer\f[], \f[C]html\f[] or \f[C]html5\f[] for output only), -pandoc will treat the document as literate Haskell source. +If you append \f[C]+lhs\f[] (or \f[C]+literate_haskell\f[]) to an +appropriate input or output format (\f[C]markdown\f[], +\f[C]mardkown_strict\f[], \f[C]rst\f[], or \f[C]latex\f[] for input or +output; \f[C]beamer\f[], \f[C]html\f[] or \f[C]html5\f[] for output +only), pandoc will treat the document as literate Haskell source. This means that .IP \[bu] 2 In markdown input, "bird track" sections will be parsed as Haskell code @@ -1264,8 +1418,8 @@ In markdown output, code blocks with classes \f[C]haskell\f[] and \f[C]literate\f[] 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, headers will be rendered setext-style (with underlines) -rather than atx-style (with \[aq]#\[aq] characters). +In addition, headers will be rendered setext\-style (with underlines) +rather than atx\-style (with \[aq]#\[aq] characters). (This is because ghc treats \[aq]#\[aq] characters in column 1 as introducing line numbers.) .IP \[bu] 2 @@ -1288,7 +1442,7 @@ Examples: .IP .nf \f[C] -pandoc\ -f\ markdown+lhs\ -t\ html +pandoc\ \-f\ markdown+lhs\ \-t\ html \f[] .fi .PP @@ -1297,7 +1451,7 @@ writes ordinary HTML (without bird tracks). .IP .nf \f[C] -pandoc\ -f\ markdown+lhs\ -t\ html+lhs +pandoc\ \-f\ markdown+lhs\ \-t\ html+lhs \f[] .fi .PP @@ -1305,17 +1459,18 @@ writes HTML with the Haskell code in bird tracks, so it can be copied and pasted as literate Haskell source. .SH AUTHORS .PP -© 2006-2011 John MacFarlane (jgm at berkeley dot edu). +© 2006\-2011 John MacFarlane (jgm at berkeley dot 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.) - Other contributors include Recai Oktaş, Paulo Tanimoto, Peter Wang, -Andrea Rossato, Eric Kow, infinity0x, Luke Plant, shreevatsa.public, -Puneeth Chaganti, Paul Rivier, rodja.trappe, Bradley Kuhn, thsutton, -Nathan Gass, Jonathan Daugherty, Jérémy Bobbio, Justin Bogner, qerub, +(See COPYRIGHT for full copyright and warranty notices.) Other +contributors include Recai Oktaş, Paulo Tanimoto, Peter Wang, Andrea +Rossato, Eric Kow, infinity0x, Luke Plant, shreevatsa.public, Puneeth +Chaganti, Paul Rivier, rodja.trappe, Bradley Kuhn, thsutton, Nathan +Gass, Jonathan Daugherty, Jérémy Bobbio, Justin Bogner, qerub, Christopher Sawicki, Kelsey Hightower, Masayoshi Takahashi, Antoine Latter, Ralf Stephan, Eric Seidel, B. -Scott Michel, Gavin Beatty. +Scott Michel, Gavin Beatty, Sergey Astanin, Arlo O\[aq]Keeffe, Denis +Laxalde, Brent Yorgey. .SH PANDOC'S MARKDOWN For a complete description of pandoc's extensions to standard markdown, see \f[C]pandoc_markdown\f[] (5). diff --git a/man/man5/pandoc_markdown.5 b/man/man5/pandoc_markdown.5 index 05f5cc467..57100c652 100644 --- a/man/man5/pandoc_markdown.5 +++ b/man/man5/pandoc_markdown.5 @@ -1,5 +1,5 @@ .\"t -.TH PANDOC_MARKDOWN 5 "January 27, 2012" "Pandoc" +.TH PANDOC_MARKDOWN 5 "January 19, 2013" "Pandoc" .SH NAME pandoc_markdown - markdown syntax for pandoc(1) .SH DESCRIPTION @@ -8,18 +8,23 @@ Pandoc understands an extended and slightly revised version of John Gruber\[aq]s markdown syntax. This document explains the syntax, noting differences from standard markdown. -Except where noted, these differences can be suppressed by specifying -the \f[C]--strict\f[] command-line option. +Except where noted, these differences can be suppressed by using the +\f[C]markdown_strict\f[] format instead of \f[C]markdown\f[]. +An extensions can be enabled by adding \f[C]+EXTENSION\f[] to the format +name and disabled by adding \f[C]\-EXTENSION\f[]. +For example, \f[C]markdown_strict+footnotes\f[] is strict markdown with +footnotes enabled, while \f[C]markdown\-footnotes\-pipe_tables\f[] is +pandoc\[aq]s markdown without footnotes or pipe tables. .SH PHILOSOPHY .PP 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 +A Markdown\-formatted document should be publishable as\-is, as plain text, without looking like it\[aq]s been marked up with tags or formatting instructions. --- John Gruber +\-\- John Gruber .RE .PP This principle has guided pandoc\[aq]s decisions in finding syntax for @@ -30,7 +35,7 @@ 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 +and provides other, non\-HTMLish ways of representing important document elements like definition lists, tables, mathematics, and footnotes. .SH PARAGRAPHS .PP @@ -39,51 +44,57 @@ line. 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, or type a backslash followed by a newline. +line. +.PP +\f[B]Extension: \f[C]escaped_line_breaks\f[]\f[] +.PP +A backslash followed by a newline is also a hard line break. .SH HEADERS .PP There are two kinds of headers, Setext and atx. -.SS Setext-style headers +.SS Setext\-style headers .PP -A setext-style header is a line of text "underlined" with a row of -\f[C]=\f[] signs (for a level one header) of \f[C]-\f[] signs (for a +A setext\-style header is a line of text "underlined" with a row of +\f[C]=\f[] signs (for a level one header) of \f[C]\-\f[] signs (for a level two header): .IP .nf \f[C] -A\ level-one\ header +A\ level\-one\ header ================== -A\ level-two\ header ------------------- +A\ level\-two\ header +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \f[] .fi .PP The header text can contain inline formatting, such as emphasis (see Inline formatting, below). -.SS Atx-style headers +.SS Atx\-style headers .PP -An Atx-style header consists of one to six \f[C]#\f[] signs and a line +An Atx\-style header consists of one to six \f[C]#\f[] signs and a line of text, optionally followed by any number of \f[C]#\f[] signs. The number of \f[C]#\f[] signs at the beginning of the line is the header level: .IP .nf \f[C] -##\ A\ level-two\ header +##\ A\ level\-two\ header -###\ A\ level-three\ header\ ### +###\ A\ level\-three\ header\ ### \f[] .fi .PP -As with setext-style headers, the header text can contain formatting: +As with setext\-style headers, the header text can contain formatting: .IP .nf \f[C] -#\ A\ level-one\ header\ with\ a\ [link](/url)\ and\ *emphasis* +#\ A\ level\-one\ header\ with\ a\ [link](/url)\ and\ *emphasis* \f[] .fi .PP +\f[B]Extension: \f[C]blank_before_header\f[]\f[] +.PP Standard markdown syntax does not require a blank line before a header. Pandoc does require this (except, of course, at the beginning of the document). @@ -100,11 +111,40 @@ I\ like\ several\ of\ their\ flavors\ of\ ice\ cream: .fi .SS Header identifiers in HTML, LaTeX, and ConTeXt .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]header_attributes\f[]\f[] +.PP +Headers can be assigned attributes using this syntax at the end of the +line containing the header text: +.IP +.nf +\f[C] +{#identifier\ .class\ .class\ key=value\ key=value} +\f[] +.fi +.PP +Although this syntax allows assignment of classes and key/value +attributes, only identifiers currently have any affect in the writers +(and only in some writers: HTML, LaTeX, ConTeXt, Textile, AsciiDoc). +Thus, for example, the following headers will all be assigned the +identifier \f[C]foo\f[]: +.IP +.nf +\f[C] +#\ My\ header\ {#foo} + +##\ My\ header\ ##\ \ \ \ {#foo} + +My\ other\ header\ \ \ {#foo} +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- +\f[] +.fi +.PP +(This syntax is compatible with PHP Markdown Extra.) +.PP +\f[B]Extension: \f[C]auto_identifiers\f[]\f[] .PP -Each header element in pandoc\[aq]s HTML and ConTeXt output is given a -unique identifier. -This identifier is based on the text of the header. +A header without an explicitly specified identifier will be +automatically assigned a unique identifier based on the header text. To derive the identifier from the header text, .IP \[bu] 2 Remove all formatting, links, etc. @@ -134,17 +174,17 @@ _ T{ Header identifiers in HTML T}@T{ -\f[C]header-identifiers-in-html\f[] +\f[C]header\-identifiers\-in\-html\f[] T} T{ -\f[I]Dogs\f[]?--in \f[I]my\f[] house? +\f[I]Dogs\f[]?\-\-in \f[I]my\f[] house? T}@T{ -\f[C]dogs--in-my-house\f[] +\f[C]dogs\-\-in\-my\-house\f[] T} T{ HTML, S5, or RTF? T}@T{ -\f[C]html-s5-or-rtf\f[] +\f[C]html\-s5\-or\-rtf\f[] T} T{ 3. @@ -163,11 +203,12 @@ These rules should, in most cases, allow one to determine the identifier from the header text. The exception is when several headers 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[C]-1\f[] appended; the third with -\f[C]-2\f[]; and so on. +the same identifier with \f[C]\-1\f[] appended; the third with +\f[C]\-2\f[]; and so on. .PP These identifiers are used to provide link targets in the table of -contents generated by the \f[C]--toc|--table-of-contents\f[] option. +contents generated by the \f[C]\-\-toc|\-\-table\-of\-contents\f[] +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: @@ -175,20 +216,64 @@ A link to this section, for example, might look like this: .nf \f[C] See\ the\ section\ on -[header\ identifiers](#header-identifiers-in-html). +[header\ identifiers](#header\-identifiers\-in\-html). \f[] .fi .PP Note, however, that this method of providing links to sections works only in HTML, LaTeX, and ConTeXt formats. .PP -If the \f[C]--section-divs\f[] option is specified, then each section +If the \f[C]\-\-section\-divs\f[] option is specified, then each section will be wrapped in a \f[C]div\f[] (or a \f[C]section\f[], if -\f[C]--html5\f[] was specified), and the identifier will be attached to -the enclosing \f[C]<div>\f[] (or \f[C]<section>\f[]) tag rather than the -header itself. +\f[C]\-\-html5\f[] was specified), and the identifier will be attached +to the enclosing \f[C]<div>\f[] (or \f[C]<section>\f[]) tag rather than +the header itself. This allows entire sections to be manipulated using javascript or treated differently in CSS. +.PP +\f[B]Extension: \f[C]implicit_header_references\f[]\f[] +.PP +Pandoc behaves as if reference links have been defined for each header. +So, instead of +.IP +.nf +\f[C] +[header\ identifiers](#header\-identifiers\-in\-html) +\f[] +.fi +.PP +you can simply write +.IP +.nf +\f[C] +[header\ identifiers] +\f[] +.fi +.PP +or +.IP +.nf +\f[C] +[header\ identifiers][] +\f[] +.fi +.PP +or +.IP +.nf +\f[C] +[the\ section\ on\ header\ identifiers][header\ identifiers] +\f[] +.fi +.PP +If there are multiple headers 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 +Unlike regular reference links, these references are case\-sensitive. +.PP +Note: if you have defined an explicit identifier for a header, then +implicit references to it will not work. .SH BLOCK QUOTATIONS .PP Markdown uses email conventions for quoting blocks of text. @@ -233,6 +318,8 @@ That is, block quotes can be nested: \f[] .fi .PP +\f[B]Extension: \f[C]blank_line_before_blockquote\f[]\f[] +.PP Standard markdown syntax does not require a blank line before a block quote. Pandoc does require this (except, of course, at the beginning of the @@ -240,8 +327,8 @@ document). The reason for the requirement is that it is all too easy for a \f[C]>\f[] to end up at the beginning of a line by accident (perhaps through line wrapping). -So, unless \f[C]--strict\f[] is used, the following does not produce a -nested block quote in pandoc: +So, unless the \f[C]markdown_strict\f[] format is used, the following +does not produce a nested block quote in pandoc: .IP .nf \f[C] @@ -269,12 +356,12 @@ 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 Delimited code blocks +.SS Fenced code blocks .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]fenced_code_blocks\f[]\f[] .PP In addition to standard indented code blocks, Pandoc supports -\f[I]delimited\f[] code blocks. +\f[I]fenced\f[] code blocks. These begin with a row of three or more tildes (\f[C]~\f[]) or backticks (\f[C]`\f[]) and end with a row of tildes or backticks that must be at least as long as the starting row. @@ -291,7 +378,7 @@ if\ (a\ >\ 3)\ { \f[] .fi .PP -Like regular code blocks, delimited code blocks must be separated from +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 @@ -328,8 +415,8 @@ Currently, the only output formats that uses this information are HTML and LaTeX. 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, do \f[C]pandoc\ --version\f[].) - Otherwise, the code block above will appear as follows: +(To see which languages are supported, do \f[C]pandoc\ \-\-version\f[].) +Otherwise, the code block above will appear as follows: .IP .nf \f[C] @@ -362,14 +449,50 @@ qsort\ []\ =\ [] \f[] .fi .PP -To prevent all highlighting, use the \f[C]--no-highlight\f[] flag. -To set the highlighting style, use \f[C]--highlight-style\f[]. +To prevent all highlighting, use the \f[C]\-\-no\-highlight\f[] flag. +To set the highlighting style, use \f[C]\-\-highlight\-style\f[]. +.SH LINE BLOCKS +.PP +\f[B]Extension: \f[C]line_blocks\f[]\f[] +.PP +A line block is a sequence of lines beginning with a vertical bar +(\f[C]|\f[]) 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 +.nf +\f[C] +|\ The\ limerick\ packs\ laughs\ anatomical +|\ In\ space\ that\ is\ quite\ economical. +|\ \ \ \ But\ the\ good\ ones\ I\[aq]ve\ seen +|\ \ \ \ So\ seldom\ are\ clean +|\ And\ the\ clean\ ones\ so\ seldom\ are\ comical + +|\ 200\ Main\ St. +|\ Berkeley,\ CA\ 94718 +\f[] +.fi +.PP +The lines can be hard\-wrapped if needed, but the continuation line must +begin with a space. +.IP +.nf +\f[C] +|\ The\ Right\ Honorable\ Most\ Venerable\ and\ Righteous\ Samuel\ L. +\ \ Constable,\ Jr. +|\ 200\ Main\ St. +|\ Berkeley,\ CA\ 94718 +\f[] +.fi +.PP +This syntax is borrowed from reStructuredText. .SH LISTS .SS Bullet lists .PP A bullet list is a list of bulleted list items. A bulleted list item begins with a bullet (\f[C]*\f[], \f[C]+\f[], or -\f[C]-\f[]). +\f[C]\-\f[]). Here is a simple example: .IP .nf @@ -418,9 +541,9 @@ list\ item. *\ and\ my\ second. \f[] .fi -.SS The four-space rule +.SS The four\-space rule .PP -A list item may contain multiple paragraphs and other block-level +A list item may contain multiple paragraphs and other block\-level content. However, subsequent paragraphs must be preceded by a blank line and indented four spaces or a tab. @@ -448,8 +571,8 @@ The nested list must be indented four spaces or one tab: \f[C] *\ fruits \ \ \ \ +\ apples -\ \ \ \ \ \ \ \ -\ macintosh -\ \ \ \ \ \ \ \ -\ red\ delicious +\ \ \ \ \ \ \ \ \-\ macintosh +\ \ \ \ \ \ \ \ \-\ red\ delicious \ \ \ \ +\ pears \ \ \ \ +\ peaches *\ vegetables @@ -476,16 +599,16 @@ list\ item. \f[] .fi .PP -\f[B]Note:\f[] Although the four-space rule for continuation paragraphs +\f[B]Note:\f[] Although the four\-space rule for continuation paragraphs comes from the official markdown syntax guide, the reference implementation, \f[C]Markdown.pl\f[], does not follow it. So pandoc will give different results than \f[C]Markdown.pl\f[] when authors have indented continuation paragraphs fewer than four spaces. .PP -The markdown syntax guide is not explicit whether the four-space rule -applies to \f[I]all\f[] block-level content in a list item; it only +The markdown syntax guide is not explicit whether the four\-space rule +applies to \f[I]all\f[] block\-level content in a list item; it only mentions paragraphs and code blocks. -But it implies that the rule applies to all block-level content +But it implies that the rule applies to all block\-level content (including nested lists), and pandoc interprets it that way. .SS Ordered lists .PP @@ -515,17 +638,19 @@ and this one: \f[] .fi .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]fancy_lists\f[]\f[] .PP Unlike standard 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-parentheses or period. +right\-parentheses 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.[1] .PP +\f[B]Extension: \f[C]startnum\f[]\f[] +.PP 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. @@ -568,7 +693,7 @@ If default list markers are desired, use \f[C]#.\f[]: .fi .SS Definition lists .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]definition_lists\f[]\f[] .PP Pandoc supports definition lists, using a syntax inspired by PHP Markdown Extra and reStructuredText:[2] @@ -596,8 +721,8 @@ two spaces. The body of the definition (including the first line, aside from the colon or tilde) should be indented four 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. +one or more block elements (paragraph, code block, list, etc.), each +indented four spaces or one tab stop. .PP If you leave space after the definition (as in the example above), the blocks of the definitions will be considered paragraphs. @@ -617,7 +742,7 @@ Term\ 2 .fi .SS Numbered example lists .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]example_lists\f[]\f[] .PP The special list marker \f[C]\@\f[] can be used for sequentially numbered examples. @@ -661,9 +786,9 @@ Consider this source: \f[C] +\ \ \ First +\ \ \ Second: -\ -\ \ \ Fee -\ -\ \ \ Fie -\ -\ \ \ Foe +\ \-\ \ \ Fee +\ \-\ \ \ Fie +\ \-\ \ \ Foe +\ \ \ Third \f[] @@ -678,8 +803,8 @@ it is treated as a paragraph. Since "Second" is followed by a list, and not a blank line, it isn\[aq]t treated as a paragraph. The fact that the list is followed by a blank line is irrelevant. -(Note: Pandoc works this way even when the \f[C]--strict\f[] option is -specified. +(Note: Pandoc works this way even when the \f[C]markdown_strict\f[] +format is specified. This behavior is consistent with the official markdown syntax description, even though it is different from that of \f[C]Markdown.pl\f[].) @@ -689,8 +814,8 @@ What if you want to put an indented code block after a list? .IP .nf \f[C] --\ \ \ item\ one --\ \ \ item\ two +\-\ \ \ item\ one +\-\ \ \ item\ two \ \ \ \ {\ my\ code\ block\ } \f[] @@ -700,16 +825,16 @@ Trouble! Here pandoc (like other markdown implementations) will treat \f[C]{\ my\ code\ block\ }\f[] as the second paragraph of item two, and not as a code block. .PP -To "cut off" the list after item two, you can insert some non-indented +To "cut off" the list after item two, you can insert some non\-indented content, like an HTML comment, which won\[aq]t produce visible output in any format: .IP .nf \f[C] --\ \ \ item\ one --\ \ \ item\ two +\-\ \ \ item\ one +\-\ \ \ item\ two -<!--\ end\ of\ list\ --> +<!\-\-\ end\ of\ list\ \-\-> \ \ \ \ {\ my\ code\ block\ } \f[] @@ -724,7 +849,7 @@ one big list: 2.\ \ two 3.\ \ three -<!--\ --> +<!\-\-\ \-\-> 1.\ \ uno 2.\ \ dos @@ -733,7 +858,7 @@ one big list: .fi .SH HORIZONTAL RULES .PP -A line containing a row of three or more \f[C]*\f[], \f[C]-\f[], or +A line containing a row of three or more \f[C]*\f[], \f[C]\-\f[], or \f[C]_\f[] characters (optionally separated by spaces) produces a horizontal rule: .IP @@ -741,23 +866,27 @@ horizontal rule: \f[C] *\ \ *\ \ *\ \ * ---------------- +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \f[] .fi .SH TABLES .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]simple_tables\f[], \f[C]multiline_tables\f[], +\f[C]grid_tables\f[], \f[C]pipe_tables\f[], \f[C]table_captions\f[]\f[] .PP -Three kinds of tables may be used. -All three kinds presuppose the use of a fixed-width font, such as +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 Simple tables .PP -\f[B]Simple tables\f[] look like this: +Simple tables look like this: .IP .nf \f[C] \ \ Right\ \ \ \ \ Left\ \ \ \ \ Center\ \ \ \ \ Default --------\ \ \ \ \ ------\ ----------\ \ \ ------- +\-\-\-\-\-\-\-\ \ \ \ \ \-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\ \ \ \-\-\-\-\-\-\- \ \ \ \ \ 12\ \ \ \ \ 12\ \ \ \ \ \ \ \ 12\ \ \ \ \ \ \ \ \ \ \ \ 12 \ \ \ \ 123\ \ \ \ \ 123\ \ \ \ \ \ \ 123\ \ \ \ \ \ \ \ \ \ 123 \ \ \ \ \ \ 1\ \ \ \ \ 1\ \ \ \ \ \ \ \ \ \ 1\ \ \ \ \ \ \ \ \ \ \ \ \ 1 @@ -771,10 +900,10 @@ Column alignments are determined by the position of the header text relative to the dashed line below it:[3] .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. +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. +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. @@ -796,11 +925,11 @@ For example: .IP .nf \f[C] --------\ \ \ \ \ ------\ ----------\ \ \ ------- +\-\-\-\-\-\-\-\ \ \ \ \ \-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\ \ \ \-\-\-\-\-\-\- \ \ \ \ \ 12\ \ \ \ \ 12\ \ \ \ \ \ \ \ 12\ \ \ \ \ \ \ \ \ \ \ \ \ 12 \ \ \ \ 123\ \ \ \ \ 123\ \ \ \ \ \ \ 123\ \ \ \ \ \ \ \ \ \ \ 123 \ \ \ \ \ \ 1\ \ \ \ \ 1\ \ \ \ \ \ \ \ \ \ 1\ \ \ \ \ \ \ \ \ \ \ \ \ \ 1 --------\ \ \ \ \ ------\ ----------\ \ \ ------- +\-\-\-\-\-\-\-\ \ \ \ \ \-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\ \ \ \-\-\-\-\-\-\- \f[] .fi .PP @@ -808,25 +937,26 @@ When headers are 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 Multiline tables .PP -\f[B]Multiline tables\f[] allow headers and table rows to span multiple -lines of text (but cells that span multiple columns or rows of the table -are not supported). +Multiline tables allow headers 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 .nf \f[C] -------------------------------------------------------------- +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \ 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\[aq]s\ another\ one.\ Note \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ the\ blank\ line\ between \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ rows. -------------------------------------------------------------- +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Table:\ Here\[aq]s\ the\ caption.\ It,\ too,\ may\ span multiple\ lines. @@ -852,14 +982,14 @@ Headers may be omitted in multiline tables as well as simple tables: .IP .nf \f[C] ------------\ -------\ ---------------\ ------------------------- +\-\-\-\-\-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \ \ \ First\ \ \ \ row\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 12.0\ Example\ of\ a\ row\ that \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ spans\ multiple\ lines. \ \ Second\ \ \ \ row\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 5.0\ Here\[aq]s\ another\ one.\ Note \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ the\ blank\ line\ between \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ rows. -------------------------------------------------------------- +\-\-\-\-\-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\ \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- :\ Here\[aq]s\ a\ multiline\ table\ without\ headers. \f[] @@ -868,36 +998,92 @@ Headers may be omitted in multiline tables as well as simple tables: 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 Grid tables .PP -\f[B]Grid tables\f[] look like this: +Grid tables look like this: .IP .nf \f[C] :\ Sample\ grid\ table. -+---------------+---------------+--------------------+ ++\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ |\ Fruit\ \ \ \ \ \ \ \ \ |\ Price\ \ \ \ \ \ \ \ \ |\ Advantages\ \ \ \ \ \ \ \ \ | +===============+===============+====================+ -|\ Bananas\ \ \ \ \ \ \ |\ $1.34\ \ \ \ \ \ \ \ \ |\ -\ built-in\ wrapper\ | -|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ -\ bright\ color\ \ \ \ \ | -+---------------+---------------+--------------------+ -|\ Oranges\ \ \ \ \ \ \ |\ $2.10\ \ \ \ \ \ \ \ \ |\ -\ cures\ scurvy\ \ \ \ \ | -|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ -\ tasty\ \ \ \ \ \ \ \ \ \ \ \ | -+---------------+---------------+--------------------+ +|\ Bananas\ \ \ \ \ \ \ |\ $1.34\ \ \ \ \ \ \ \ \ |\ \-\ built\-in\ wrapper\ | +|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \-\ bright\ color\ \ \ \ \ | ++\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ +|\ Oranges\ \ \ \ \ \ \ |\ $2.10\ \ \ \ \ \ \ \ \ |\ \-\ cures\ scurvy\ \ \ \ \ | +|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \-\ tasty\ \ \ \ \ \ \ \ \ \ \ \ | ++\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+ \f[] .fi .PP The row of \f[C]=\f[]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.) -\&. +paragraphs, code blocks, lists, etc.). Alignments are not supported, nor are cells that span multiple columns or rows. Grid tables can be created easily using Emacs table mode. +.SS Pipe tables +.PP +Pipe tables look like this: +.IP +.nf +\f[C] +|\ Right\ |\ Left\ |\ Default\ |\ Center\ | +|\-\-\-\-\-\-:|:\-\-\-\-\-|\-\-\-\-\-\-\-\-\-|:\-\-\-\-\-\-:| +|\ \ \ 12\ \ |\ \ 12\ \ |\ \ \ \ 12\ \ \ |\ \ \ \ 12\ \ | +|\ \ 123\ \ |\ \ 123\ |\ \ \ 123\ \ \ |\ \ \ 123\ \ | +|\ \ \ \ 1\ \ |\ \ \ \ 1\ |\ \ \ \ \ 1\ \ \ |\ \ \ \ \ 1\ \ | + +\ \ :\ Demonstration\ of\ simple\ table\ syntax. +\f[] +.fi +.PP +The syntax is the same as in PHP markdown extra. +The beginning and ending pipe characters are optional, but pipes are +required between all columns. +The colons indicate column alignment as shown. +The header can be omitted, but the horizontal line must still be +included, as it defines column alignments. +.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 +.nf +\f[C] +fruit|\ price +\-\-\-\-\-|\-\-\-\-\-: +apple|2.05 +pear|1.37 +orange|3.09 +\f[] +.fi +.PP +The cells of pipe tables cannot contain block elements like paragraphs +and lists, and cannot span multiple lines. +.PP +Note: Pandoc also recognizes pipe tables of the following form, as can +produced by Emacs\[aq] orgtbl\-mode: +.IP +.nf +\f[C] +|\ One\ |\ Two\ \ \ | +|\-\-\-\-\-+\-\-\-\-\-\-\-| +|\ my\ \ |\ table\ | +|\ is\ \ |\ nice\ \ | +\f[] +.fi +.PP +The difference is that \f[C]+\f[] is used instead of \f[C]|\f[]. +Other orgtbl features are not supported. +In particular, to get non\-default column alignment, you\[aq]ll need to +add colons as above. .SH TITLE BLOCK .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]pandoc_title_block\f[]\f[] .PP If the file begins with a title block .IP @@ -911,9 +1097,8 @@ If the file begins with a title block .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 title and an author, or all three -elements. +output.) The block may contain just a title, a title 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 @@ -957,28 +1142,27 @@ So, all of the following are equivalent: The date must fit on one line. .PP All three metadata fields may contain standard inline formatting -(italics, links, footnotes, etc.) -\&. +(italics, links, footnotes, etc.). .PP Title blocks will always be parsed, but they will affect the output only -when the \f[C]--standalone\f[] (\f[C]-s\f[]) option is chosen. -In HTML output, titles will appear twice: once in the document head -- +when the \f[C]\-\-standalone\f[] (\f[C]\-s\f[]) 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. +\-\- and once at the beginning of the document body. The title in the document head can have an optional prefix attached -(\f[C]--title-prefix\f[] or \f[C]-T\f[] option). +(\f[C]\-\-title\-prefix\f[] or \f[C]\-T\f[] 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 \f[C]-T\f[] and no title block +If a title prefix is specified with \f[C]\-T\f[] 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. +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. +Anything after this is assumed to be additional footer and header text. A single pipe character (\f[C]|\f[]) should be used to separate the footer text from the header text. Thus, @@ -1008,6 +1192,8 @@ will also have "Pandoc User Manuals" in the footer. will also have "Version 4.0" in the header. .SH BACKSLASH ESCAPES .PP +\f[B]Extension: \f[C]all_symbols_escapable\f[]\f[] +.PP 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. @@ -1036,22 +1222,22 @@ instead of .fi .PP This rule is easier to remember than standard markdown\[aq]s rule, which -allows only the following characters to be backslash-escaped: +allows only the following characters to be backslash\-escaped: .IP .nf \f[C] -\\`*_{}[]()>#+-.! +\\`*_{}[]()>#+\-.! \f[] .fi .PP -(However, if the \f[C]--strict\f[] option is supplied, the standard +(However, if the \f[C]markdown_strict\f[] format is used, the standard markdown rule will be used.) .PP -A backslash-escaped space is parsed as a nonbreaking space. +A backslash\-escaped space is parsed as a nonbreaking space. It will appear in TeX output as \f[C]~\f[] and in HTML and XML as \f[C]\\ \f[] or \f[C]\\ \f[]. .PP -A backslash-escaped newline (i.e. +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[C]\\\\\f[] and in HTML as @@ -1062,11 +1248,11 @@ indicating hard line breaks using two trailing spaces on a line. Backslash escapes do not work in verbatim contexts. .SH SMART PUNCTUATION .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension\f[] .PP -If the \f[C]--smart\f[] option is specified, pandoc will produce +If the \f[C]\-\-smart\f[] option is specified, pandoc will produce typographically correct output, converting straight quotes to curly -quotes, \f[C]---\f[] to em-dashes, \f[C]--\f[] to en-dashes, and +quotes, \f[C]\-\-\-\f[] to em\-dashes, \f[C]\-\-\f[] to en\-dashes, and \f[C]\&...\f[] to ellipses. Nonbreaking spaces are inserted after certain abbreviations, such as "Mr." @@ -1096,7 +1282,7 @@ This\ is\ **strong\ emphasis**\ and\ __with\ underscores__. .fi .PP A \f[C]*\f[] or \f[C]_\f[] character surrounded by spaces, or -backslash-escaped, will not trigger emphasis: +backslash\-escaped, will not trigger emphasis: .IP .nf \f[C] @@ -1104,6 +1290,8 @@ This\ is\ *\ not\ emphasized\ *,\ and\ \\*neither\ is\ this\\*. \f[] .fi .PP +\f[B]Extension: \f[C]intraword_underscores\f[]\f[] +.PP Because \f[C]_\f[] is sometimes used inside words and identifiers, pandoc does not interpret a \f[C]_\f[] surrounded by alphanumeric characters as an emphasis marker. @@ -1116,7 +1304,7 @@ feas*ible*,\ not\ feas*able*. .fi .SS Strikeout .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]strikeout\f[]\f[] .PP To strikeout a section of text with a horizontal line, begin and end it with \f[C]~~\f[]. @@ -1129,7 +1317,7 @@ This\ ~~is\ deleted\ text.~~ .fi .SS Superscripts and subscripts .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]superscript\f[], \f[C]subscript\f[]\f[] .PP Superscripts may be written by surrounding the superscripted text by \f[C]^\f[] characters; subscripts may be written by surrounding the @@ -1145,9 +1333,9 @@ H~2~O\ is\ a\ liquid.\ \ 2^10^\ is\ 1024. 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[C]~\f[] and \f[C]^\f[].) - Thus, if you want the letter P with \[aq]a cat\[aq] in subscripts, use -\f[C]P~a\\\ cat~\f[], not \f[C]P~a\ cat~\f[]. +the ordinary use of \f[C]~\f[] and \f[C]^\f[].) Thus, if you want the +letter P with \[aq]a cat\[aq] in subscripts, use \f[C]P~a\\\ cat~\f[], +not \f[C]P~a\ cat~\f[]. .SS Verbatim .PP To make a short span of text verbatim, put it inside backticks: @@ -1173,7 +1361,7 @@ 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 +Note that backslash\-escapes (and other markdown constructs) do not work in verbatim contexts: .IP .nf @@ -1182,7 +1370,9 @@ This\ is\ a\ backslash\ followed\ by\ an\ asterisk:\ `\\*`. \f[] .fi .PP -Attributes can be attached to verbatim text, just as with delimited code +\f[B]Extension: \f[C]inline_code_attributes\f[]\f[] +.PP +Attributes can be attached to verbatim text, just as with fenced code blocks: .IP .nf @@ -1192,7 +1382,7 @@ blocks: .fi .SH MATH .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]tex_math_dollars\f[]\f[] .PP Anything between two \f[C]$\f[] characters will be treated as TeX math. The opening \f[C]$\f[] must have a character immediately to its right, @@ -1200,13 +1390,13 @@ while the closing \f[C]$\f[] must have a character immediately to its left. Thus, \f[C]$20,000\ and\ $30,000\f[] won\[aq]t parse as math. If for some reason you need to enclose text in literal \f[C]$\f[] -characters, backslash-escape them and they won\[aq]t be treated as math +characters, backslash\-escape them and they won\[aq]t be treated as math delimiters. .PP TeX math will be printed in all output formats. How it is rendered depends on the output format: .TP -.B Markdown, LaTeX, Org-Mode, ConTeXt +.B Markdown, LaTeX, Org\-Mode, ConTeXt It will appear verbatim between \f[C]$\f[] characters. .RS .RE @@ -1249,8 +1439,8 @@ otherwise appear verbatim. .RE .TP .B Docbook -If the \f[C]--mathml\f[] flag is used, it will be rendered using mathml -in an \f[C]inlineequation\f[] or \f[C]informalequation\f[] tag. +If the \f[C]\-\-mathml\f[] flag is used, it will be rendered using +mathml in an \f[C]inlineequation\f[] or \f[C]informalequation\f[] tag. Otherwise it will be rendered, if possible, using unicode characters. .RS .RE @@ -1260,9 +1450,17 @@ It will be rendered using OMML math markup. .RS .RE .TP -.B HTML, Slidy, Slideous, DZSlides, S5, EPUB -The way math is rendered in HTML will depend on the command-line options -selected: +.B FictionBook2 +If the \f[C]\-\-webtex\f[] option is used, formulas are rendered as +images using Google Charts or other compatible web service, downloaded +and embedded in the e\-book. +Otherwise, they will appear verbatim. +.RS +.RE +.TP +.B HTML, Slidy, DZSlides, S5, EPUB +The way math is rendered in HTML will depend on the command\-line +options selected: .RS .IP "1." 3 The default is to render TeX math as far as possible using unicode @@ -1270,28 +1468,28 @@ characters, as with RTF, DocBook, and OpenDocument output. Formulas are put inside a \f[C]span\f[] with \f[C]class="math"\f[], so that they may be styled differently from the surrounding text if needed. .IP "2." 3 -If the \f[C]--latexmathml\f[] option is used, TeX math will be displayed -between $ or $$ characters and put in \f[C]<span>\f[] tags with class -\f[C]LaTeX\f[]. +If the \f[C]\-\-latexmathml\f[] option is used, TeX math will be +displayed between \f[C]$\f[] or \f[C]$$\f[] characters and put in +\f[C]<span>\f[] tags with class \f[C]LaTeX\f[]. The LaTeXMathML script will be used to render it as formulas. (This trick does not work in all browsers, but it works in Firefox. In browsers that do not support LaTeXMathML, TeX math will appear -verbatim between $ characters.) +verbatim between \f[C]$\f[] characters.) .IP "3." 3 -If the \f[C]--jsmath\f[] option is used, TeX math will be put inside +If the \f[C]\-\-jsmath\f[] option is used, TeX math will be put inside \f[C]<span>\f[] tags (for inline math) or \f[C]<div>\f[] tags (for display math) with class \f[C]math\f[]. The jsMath script will be used to render it. .IP "4." 3 -If the \f[C]--mimetex\f[] option is used, the mimeTeX CGI script will be -called to generate images for each TeX formula. +If the \f[C]\-\-mimetex\f[] option is used, the mimeTeX CGI script will +be called to generate images for each TeX formula. This should work in all browsers. -The \f[C]--mimetex\f[] option takes an optional URL as argument. +The \f[C]\-\-mimetex\f[] option takes an optional URL as argument. If no URL is specified, it will be assumed that the mimeTeX CGI script -is at \f[C]/cgi-bin/mimetex.cgi\f[]. +is at \f[C]/cgi\-bin/mimetex.cgi\f[]. .IP "5." 3 -If the \f[C]--gladtex\f[] option is used, TeX formulas will be enclosed -in \f[C]<eq>\f[] tags in the HTML output. +If the \f[C]\-\-gladtex\f[] option is used, TeX formulas will be +enclosed in \f[C]<eq>\f[] tags in the HTML output. The resulting \f[C]htex\f[] file may then be processed by gladTeX, which will produce image files for each formula and an \f[C]html\f[] file with links to these images. @@ -1300,31 +1498,41 @@ So, the procedure is: .IP .nf \f[C] -pandoc\ -s\ --gladtex\ myfile.txt\ -o\ myfile.htex -gladtex\ -d\ myfile-images\ myfile.htex -#\ produces\ myfile.html\ and\ images\ in\ myfile-images +pandoc\ \-s\ \-\-gladtex\ myfile.txt\ \-o\ myfile.htex +gladtex\ \-d\ myfile\-images\ myfile.htex +#\ produces\ myfile.html\ and\ images\ in\ myfile\-images \f[] .fi .RE .IP "6." 3 -If the \f[C]--webtex\f[] option is used, TeX formulas will be converted -to \f[C]<img>\f[] tags that link to an external script that converts -formulas to images. -The formula will be URL-encoded and concatenated with the URL provided. +If the \f[C]\-\-webtex\f[] option is used, TeX formulas will be +converted to \f[C]<img>\f[] tags that link to an external script that +converts formulas to images. +The formula will be URL\-encoded and concatenated with the URL provided. If no URL is specified, the Google Chart API will be used (\f[C]http://chart.apis.google.com/chart?cht=tx&chl=\f[]). +.IP "7." 3 +If the \f[C]\-\-mathjax\f[] option is used, TeX math will be displayed +between \f[C]\\(...\\)\f[] (for inline math) or \f[C]\\[...\\]\f[] (for +display math) and put in \f[C]<span>\f[] tags with class \f[C]math\f[]. +The MathJax script will be used to render it as formulas. .RE .SH RAW HTML .PP +\f[B]Extension: \f[C]raw_html\f[]\f[] +.PP Markdown allows you to insert raw HTML (or DocBook) anywhere in a document (except verbatim contexts, where \f[C]<\f[], \f[C]>\f[], and \f[C]&\f[] are interpreted literally). +(Techncially 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, and Textile output, and suppressed in other formats. .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]markdown_in_html_blocks\f[]\f[] .PP Standard markdown allows you to include HTML "blocks": blocks of HTML between balanced tags that are separated from the surrounding text with @@ -1332,8 +1540,9 @@ blank lines, and start and end at the left margin. Within these blocks, everything is interpreted as HTML, not markdown; so (for example), \f[C]*\f[] does not signify emphasis. .PP -Pandoc behaves this way when \f[C]--strict\f[] is specified; but by -default, pandoc interprets material between HTML block tags as markdown. +Pandoc behaves this way when the \f[C]markdown_strict\f[] format is +used; but by default, pandoc interprets material between HTML block tags +as markdown. Thus, for example, Pandoc will turn .IP .nf @@ -1372,7 +1581,7 @@ For example, one can surround a block of markdown text with markdown. .SH RAW TEX .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]raw_tex\f[]\f[] .PP In addition to raw HTML, pandoc allows raw LaTeX, TeX, and ConTeXt to be included in a document. @@ -1392,9 +1601,9 @@ Note that in LaTeX environments, like \f[C] \\begin{tabular}{|l|l|}\\hline Age\ &\ Frequency\ \\\\\ \\hline -18--25\ \ &\ 15\ \\\\ -26--35\ \ &\ 33\ \\\\\ -36--45\ \ &\ 22\ \\\\\ \\hline +18\-\-25\ \ &\ 15\ \\\\ +26\-\-35\ \ &\ 33\ \\\\ +36\-\-45\ \ &\ 22\ \\\\\ \\hline \\end{tabular} \f[] .fi @@ -1404,7 +1613,9 @@ LaTeX, not as markdown. .PP Inline LaTeX is ignored in output formats other than Markdown, LaTeX, and ConTeXt. -.SS Macros +.SH LATEX MACROS +.PP +\f[B]Extension: \f[C]latex_macros\f[]\f[] .PP For output formats other than LaTeX, pandoc will parse LaTeX \f[C]\\newcommand\f[] and \f[C]\\renewcommand\f[] definitions and apply @@ -1461,12 +1672,10 @@ before or after the link). .PP The link consists of link text in square brackets, followed by a label in square brackets. -(There can be space between the two.) - The link definition must begin at the left margin or indented no more -than three spaces. -It consists of the bracketed label, followed by a colon and a space, -followed by the URL, and optionally (after a space) a link title either -in quotes or in parentheses. +(There can be space between the two.) The link definition consists of +the bracketed label, followed by a colon and a space, followed by the +URL, and optionally (after a space) a link title either in quotes or in +parentheses. .PP Here are some examples: .IP @@ -1517,6 +1726,21 @@ See\ [my\ website][],\ or\ [my\ website]. [my\ website]:\ http://foo.bar.baz \f[] .fi +.PP +Note: In \f[C]Markdown.pl\f[] and most other markdown implementations, +reference link definitions cannot occur in nested constructions such as +list items or block quotes. +Pandoc lifts this arbitrary seeming restriction. +So the following is fine in pandoc, though not in most other +implementations: +.IP +.nf +\f[C] +>\ My\ block\ [quote]. +> +>\ [quote]:\ /foo +\f[] +.fi .SS Internal links .PP To link to another section of the same document, use the automatically @@ -1558,14 +1782,13 @@ The link text will be used as the image\[aq]s alt text: .fi .SS Pictures with captions .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]implicit_figures\f[]\f[] .PP An image occurring by itself in a paragraph will be rendered as a figure with a caption.[4] (In LaTeX, a figure environment will be used; in HTML, the image will be placed in a \f[C]div\f[] with class \f[C]figure\f[], together with a caption in a \f[C]p\f[] with class -\f[C]caption\f[].) - The image\[aq]s alt text will be used as the caption. +\f[C]caption\f[].) The image\[aq]s alt text will be used as the caption. .IP .nf \f[C] @@ -1579,12 +1802,12 @@ One way to do this is to insert a nonbreaking space after the image: .IP .nf \f[C] -![This\ image\ won\[aq]t\ be\ a\ figure](/url/of/image.png)\\\ +![This\ image\ won\[aq]t\ be\ a\ figure](/url/of/image.png)\\ \f[] .fi .SH FOOTNOTES .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]footnotes\f[]\f[] .PP Pandoc\[aq]s markdown allows footnotes, using the following syntax: .IP @@ -1596,14 +1819,14 @@ Here\ is\ a\ footnote\ reference,[^1]\ and\ another.[^longnote] [^longnote]:\ Here\[aq]s\ one\ with\ multiple\ blocks. -\ \ \ \ Subsequent\ paragraphs\ are\ indented\ to\ show\ that\ they\ +\ \ \ \ Subsequent\ paragraphs\ are\ indented\ to\ show\ that\ they belong\ to\ the\ previous\ footnote. \ \ \ \ \ \ \ \ {\ some.code\ } \ \ \ \ The\ whole\ paragraph\ can\ be\ indented,\ or\ just\ the\ first -\ \ \ \ line.\ \ In\ this\ way,\ multi-paragraph\ footnotes\ work\ like -\ \ \ \ multi-paragraph\ list\ items. +\ \ \ \ line.\ \ In\ this\ way,\ multi\-paragraph\ footnotes\ work\ like +\ \ \ \ multi\-paragraph\ list\ items. This\ paragraph\ won\[aq]t\ be\ part\ of\ the\ note,\ because\ it isn\[aq]t\ indented. @@ -1617,8 +1840,9 @@ the note itself; in the output, footnotes will be numbered sequentially. .PP The footnotes themselves need not be placed at the end of the document. They may appear anywhere except inside other block elements (lists, -block quotes, tables, etc.) -\&. +block quotes, tables, etc.). +.PP +\f[B]Extension: \f[C]inline_notes\f[]\f[] .PP Inline footnotes are also allowed (though, unlike regular notes, they cannot contain multiple paragraphs). @@ -1635,10 +1859,10 @@ note.] Inline and regular footnotes may be mixed freely. .SH CITATIONS .PP -\f[I]Pandoc extension\f[]. +\f[B]Extension: \f[C]citations\f[]\f[] .PP Pandoc can automatically generate citations and a bibliography in a -number of styles (using Andrea Rossato\[aq]s \f[C]hs-citeproc\f[]). +number of styles (using Andrea Rossato\[aq]s \f[C]hs\-citeproc\f[]). In order to use this feature, you will need a bibliographic database in one of the following formats: .PP @@ -1657,11 +1881,16 @@ T}@T{ \&.mods T} T{ -BibTeX/BibLaTeX +BibLaTeX T}@T{ \&.bib T} T{ +BibTeX +T}@T{ +\&.bibtex +T} +T{ RIS T}@T{ \&.ris @@ -1698,19 +1927,22 @@ T}@T{ T} .TE .PP +Note that \f[C]\&.bib\f[] can generally be used with both BibTeX and +BibLaTeX files, but you can use \f[C]\&.bibtex\f[] to force BibTeX. +.PP You will need to specify the bibliography file using the -\f[C]--bibliography\f[] command-line option (which may be repeated if +\f[C]\-\-bibliography\f[] command\-line option (which may be repeated if you have several bibliographies). .PP -By default, pandoc will use a Chicago author-date format for citations +By default, pandoc will use a Chicago author\-date format for citations and references. -To use another style, you will need to use the \f[C]--csl\f[] option to -specify a CSL 1.0 style file. +To use another style, you will need to use the \f[C]\-\-csl\f[] option +to specify a CSL 1.0 style file. A primer on creating and modifying CSL styles can be found at -\f[C]http://citationstyles.org/downloads/primer.html\f[]. +http://citationstyles.org/downloads/primer.html. A repository of CSL styles can be found at -\f[C]https://github.com/citation-style-language/styles\f[]. -See also \f[C]http://zotero.org/styles\f[] for easy browsing. +https://github.com/citation\-style\-language/styles. +See also http://zotero.org/styles for easy browsing. .PP Citations go inside square brackets and are separated by semicolons. Each citation must have a key, composed of \[aq]\@\[aq] + the citation @@ -1720,25 +1952,25 @@ Here are some examples: .IP .nf \f[C] -Blah\ blah\ [see\ \@doe99,\ pp.\ 33-35;\ also\ \@smith04,\ ch.\ 1]. +Blah\ blah\ [see\ \@doe99,\ pp.\ 33\-35;\ also\ \@smith04,\ ch.\ 1]. -Blah\ blah\ [\@doe99,\ pp.\ 33-35,\ 38-39\ and\ *passim*]. +Blah\ blah\ [\@doe99,\ pp.\ 33\-35,\ 38\-39\ and\ *passim*]. Blah\ blah\ [\@smith04;\ \@doe99]. \f[] .fi .PP -A minus sign (\f[C]-\f[]) before the \f[C]\@\f[] will suppress mention +A minus sign (\f[C]\-\f[]) before the \f[C]\@\f[] will suppress mention of the author in the citation. This can be useful when the author is already mentioned in the text: .IP .nf \f[C] -Smith\ says\ blah\ [-\@smith04]. +Smith\ says\ blah\ [\-\@smith04]. \f[] .fi .PP -You can also write an in-text citation, as follows: +You can also write an in\-text citation, as follows: .IP .nf \f[C] diff --git a/pandoc.cabal b/pandoc.cabal index 101a040bf..61b72565e 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,10 +1,10 @@ Name: pandoc -Version: 1.9.4.5 +Version: 1.10.0.3 Cabal-Version: >= 1.10 Build-Type: Custom License: GPL License-File: COPYING -Copyright: (c) 2006-2012 John MacFarlane +Copyright: (c) 2006-2013 John MacFarlane Author: John MacFarlane <jgm@berkeley.edu> Maintainer: John MacFarlane <jgm@berkeley.edu> Bug-Reports: https://github.com/jgm/pandoc/issues @@ -16,11 +16,12 @@ Synopsis: Conversion between markup formats Description: Pandoc is a Haskell library for converting from one markup format to another, and a command-line tool that uses this library. It can read markdown and (subsets of) HTML, - reStructuredText, LaTeX, DocBook, and Textile, and it can write - markdown, reStructuredText, HTML, LaTeX, ConTeXt, Docbook, - OpenDocument, ODT, Word docx, RTF, MediaWiki, Textile, - groff man pages, plain text, Emacs Org-Mode, AsciiDoc, EPUB, - and S5, Slidy and Slideous HTML slide shows. + reStructuredText, LaTeX, DocBook, MediaWiki markup, + and Textile, and it can write markdown, reStructuredText, + HTML, LaTeX, ConTeXt, Docbook, OpenDocument, ODT, + Word docx, RTF, MediaWiki, Textile, groff man pages, + plain text, Emacs Org-Mode, AsciiDoc, EPUB (v2 and v3), + FictionBook2, and S5, Slidy and Slideous HTML slide shows. . Pandoc extends standard markdown syntax with footnotes, embedded LaTeX, definition lists, tables, and other @@ -37,55 +38,58 @@ Description: Pandoc is a Haskell library for converting from one markup only adding a reader or writer. Data-Files: -- templates - templates/default.html, templates/default.html5, - templates/default.docbook, templates/default.beamer, - templates/default.opendocument, templates/default.latex, - templates/default.context, templates/default.texinfo, - templates/default.man, templates/default.markdown, - templates/default.rst, templates/default.plain, - templates/default.mediawiki, templates/default.rtf, - templates/default.s5, templates/default.slidy, - templates/default.slideous, - templates/default.dzslides, templates/default.asciidoc, - templates/default.textile, templates/default.org, - templates/epub-titlepage.html, templates/epub-page.html, - templates/epub-coverimage.html, + data/templates/default.html, data/templates/default.html5, + data/templates/default.docbook, data/templates/default.beamer, + data/templates/default.opendocument, + data/templates/default.latex, + data/templates/default.context, + data/templates/default.texinfo, + data/templates/default.man, + data/templates/default.markdown, + data/templates/default.rst, data/templates/default.plain, + data/templates/default.mediawiki, data/templates/default.rtf, + data/templates/default.s5, data/templates/default.slidy, + data/templates/default.slideous, + data/templates/default.dzslides, + data/templates/default.asciidoc, + data/templates/default.textile, data/templates/default.org, + data/templates/default.epub, data/templates/default.epub3, -- data for ODT writer - reference.odt, + data/reference.odt, -- data for docx writer - reference.docx, + data/reference.docx, -- stylesheet for EPUB writer - epub.css, + data/epub.css, -- data for LaTeXMathML writer data/LaTeXMathML.js, data/MathMLinHTML.js, -- data for S5 writer - s5/default/slides.js, - s5/default/s5-core.css, - s5/default/framing.css, - s5/default/pretty.css, - s5/default/opera.css, - s5/default/outline.css, - s5/default/print.css, - s5/default/slides.css, - s5/default/iepngfix.htc, - s5/default/blank.gif, - s5/default/bodybg.gif, + data/s5/default/slides.js, + data/s5/default/s5-core.css, + data/s5/default/framing.css, + data/s5/default/pretty.css, + data/s5/default/opera.css, + data/s5/default/outline.css, + data/s5/default/print.css, + data/s5/default/slides.css, + data/s5/default/iepngfix.htc, + data/s5/default/blank.gif, + data/s5/default/bodybg.gif, -- data for slidy writer - slidy/styles/slidy.css, - slidy/scripts/slidy.js.gz, - slidy/graphics/fold.gif, - slidy/graphics/unfold.gif, - slidy/graphics/nofold-dim.gif, - slidy/graphics/unfold-dim.gif, - slidy/graphics/fold-dim.gif, + data/slidy/styles/slidy.css, + data/slidy/scripts/slidy.js.gz, + data/slidy/graphics/fold.gif, + data/slidy/graphics/unfold.gif, + data/slidy/graphics/nofold-dim.gif, + data/slidy/graphics/unfold-dim.gif, + data/slidy/graphics/fold-dim.gif, -- data for slideous writer - slideous/slideous.css, - slideous/slideous.js, + data/slideous/slideous.css, + data/slideous/slideous.js, -- data for dzslides writer - dzslides/template.html, + data/dzslides/template.html, -- data for citeproc - default.csl, + data/default.csl, -- documentation README, INSTALL, COPYRIGHT, BUGS, changelog Extra-Source-Files: @@ -95,8 +99,6 @@ Extra-Source-Files: -- generated man pages (produced post-build) man/man1/pandoc.1, man/man5/pandoc_markdown.5, - -- benchmarks - Benchmark.hs, -- tests tests/bodybg.gif, tests/docbook-reader.docbook @@ -121,6 +123,8 @@ Extra-Source-Files: tests/markdown-citations.mhra.txt, tests/markdown-citations.ieee.txt, tests/textile-reader.textile, + tests/mediawiki-reader.wiki, + tests/mediawiki-reader.native, tests/rst-reader.native, tests/rst-reader.rst, tests/s5.basic.html, @@ -146,6 +150,7 @@ Extra-Source-Files: tests/tables.rtf, tests/tables.txt, tests/tables-rstsubset.native, + tests/tables.fb2, tests/testsuite.native, tests/testsuite.txt, tests/writer.latex, @@ -164,7 +169,9 @@ Extra-Source-Files: tests/writer.rst, tests/writer.rtf, tests/writer.texinfo, + tests/writer.fb2, tests/lhs-test.native, + tests/lhs-test-markdown.native, tests/lhs-test.markdown, tests/lhs-test.markdown+lhs, tests/lhs-test.rst, @@ -173,7 +180,21 @@ Extra-Source-Files: tests/lhs-test.latex+lhs, tests/lhs-test.html, tests/lhs-test.html+lhs, - tests/lhs-test.fragment.html+lhs + tests/lhs-test.fragment.html+lhs, + tests/pipe-tables.txt, + tests/pipe-tables.native, + tests/fb2.basic.markdown, + tests/fb2.basic.fb2, + tests/fb2.titles.markdown, + tests/fb2.titles.fb2, + tests/fb2.images.markdown, + tests/fb2.images.fb2, + tests/fb2.images-embedded.html, + tests/fb2.images-embedded.fb2, + tests/fb2.math.markdown, + tests/fb2.math.fb2, + tests/fb2.test-small.png, + tests/fb2.test.jpg Extra-Tmp-Files: man/man1/pandoc.1, man/man5/pandoc_markdown.5 @@ -181,30 +202,26 @@ Source-repository head type: git location: git://github.com/jgm/pandoc.git -Flag executable - Description: Build the pandoc executable. - Default: True -Flag library - Description: Build the pandoc library. - Default: True Flag blaze_html_0_5 Description: Use blaze-html 0.5 and blaze-markup 0.5 + Default: True +Flag embed_data_files + Description: Embed data files in binary for relocatable executable. Default: False Library - -- Note: the following is duplicated in all stanzas. - -- It needs to be duplicated because of the library & executable flags. - -- BEGIN DUPLICATED SECTION - Build-Depends: containers >= 0.1 && < 0.6, + Build-Depends: base >= 4.2 && <5, + syb >= 0.1 && < 0.4, + containers >= 0.1 && < 0.6, parsec >= 3.1 && < 3.2, mtl >= 1.1 && < 2.2, network >= 2 && < 2.5, filepath >= 1.1 && < 1.4, process >= 1 && < 1.2, directory >= 1 && < 1.3, - bytestring >= 0.9 && < 1.0, + bytestring >= 0.9 && < 0.11, + text >= 0.11 && < 0.12, zip-archive >= 0.1.1.7 && < 0.2, - utf8-string >= 0.3 && < 0.4, old-locale >= 1 && < 1.1, time >= 1.2 && < 1.5, HTTP >= 4000.0.5 && < 4000.3, @@ -212,13 +229,14 @@ Library xml >= 1.3.12 && < 1.4, random >= 1 && < 1.1, extensible-exceptions >= 0.1 && < 0.2, - citeproc-hs >= 0.3.4 && < 0.4, - pandoc-types >= 1.9.0.2 && < 1.10, + citeproc-hs >= 0.3.6 && < 0.4, + pandoc-types >= 1.10 && < 1.11, json >= 0.4 && < 0.8, tagsoup >= 0.12.5 && < 0.13, base64-bytestring >= 0.1 && < 1.1, zlib >= 0.5 && < 0.6, highlighting-kate >= 0.5.1 && < 0.6, + data-default >= 0.4 && < 0.6, temporary >= 1.1 && < 1.2 if flag(blaze_html_0_5) build-depends: @@ -227,17 +245,17 @@ Library else build-depends: blaze-html >= 0.4.3.0 && < 0.5 - if impl(ghc >= 6.10) - Build-depends: base >= 4 && < 5, syb >= 0.1 && < 0.4 - else - Build-depends: base >= 3 && < 4 + if flag(embed_data_files) + build-depends: file-embed >= 0.0.4.7 && < 0.1, + template-haskell >= 2.4 && < 2.9 + cpp-options: -DEMBED_DATA_FILES if impl(ghc >= 7.0.1) - Ghc-Options: -O2 -rtsopts -Wall -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -rtsopts -Wall -fno-warn-unused-do-bind else if impl(ghc >= 6.12) - Ghc-Options: -O2 -Wall -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -Wall -fno-warn-unused-do-bind else - Ghc-Options: -O2 -Wall + Ghc-Options: -Wall if impl(ghc >= 7.0.1) Ghc-Prof-Options: -auto-all -caf-all -rtsopts else @@ -249,9 +267,9 @@ Library RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances Hs-Source-Dirs: src - -- END DUPLICATED SECTION Exposed-Modules: Text.Pandoc, + Text.Pandoc.Options, Text.Pandoc.Pretty, Text.Pandoc.Shared, Text.Pandoc.Parsing, @@ -259,6 +277,7 @@ Library Text.Pandoc.Readers.HTML, Text.Pandoc.Readers.LaTeX, Text.Pandoc.Readers.Markdown, + Text.Pandoc.Readers.MediaWiki, Text.Pandoc.Readers.RST, Text.Pandoc.Readers.DocBook, Text.Pandoc.Readers.TeXMath, @@ -282,95 +301,60 @@ Library Text.Pandoc.Writers.ODT, Text.Pandoc.Writers.Docx, Text.Pandoc.Writers.EPUB, + Text.Pandoc.Writers.FB2, Text.Pandoc.PDF, + Text.Pandoc.UTF8, Text.Pandoc.Templates, + Text.Pandoc.XML, Text.Pandoc.Biblio, - Text.Pandoc.UTF8, Text.Pandoc.SelfContained - Other-Modules: Text.Pandoc.XML, - Text.Pandoc.MIME, + Other-Modules: Text.Pandoc.MIME, Text.Pandoc.UUID, Text.Pandoc.ImageSize, Text.Pandoc.Slides, Paths_pandoc - if flag(library) - Buildable: True - else - Buildable: False + Buildable: True Executable pandoc - -- Note: the following is duplicated in all stanzas. - -- It needs to be duplicated because of the library & executable flags. - -- BEGIN DUPLICATED SECTION - Build-Depends: containers >= 0.1 && < 0.6, - parsec >= 3.1 && < 3.2, - mtl >= 1.1 && < 2.2, - network >= 2 && < 2.5, - filepath >= 1.1 && < 1.4, - process >= 1 && < 1.2, + Build-Depends: pandoc, + base >= 4.2 && <5, directory >= 1 && < 1.3, - bytestring >= 0.9 && < 1.0, - zip-archive >= 0.1.1.7 && < 0.2, - utf8-string >= 0.3 && < 0.4, - old-locale >= 1 && < 1.1, - time >= 1.2 && < 1.5, - HTTP >= 4000.0.5 && < 4000.3, - texmath >= 0.6.0.2 && < 0.7, - xml >= 1.3.12 && < 1.4, - random >= 1 && < 1.1, + filepath >= 1.1 && < 1.4, + network >= 2 && < 2.5, + text >= 0.11 && < 0.12, + bytestring >= 0.9 && < 0.11, extensible-exceptions >= 0.1 && < 0.2, - citeproc-hs >= 0.3.4 && < 0.4, - pandoc-types >= 1.9.0.2 && < 1.10, - json >= 0.4 && < 0.8, - tagsoup >= 0.12.5 && < 0.13, - base64-bytestring >= 0.1 && < 1.1, - zlib >= 0.5 && < 0.6, highlighting-kate >= 0.5.1 && < 0.6, - temporary >= 1.1 && < 1.2 - if flag(blaze_html_0_5) - build-depends: - blaze-html >= 0.5 && < 0.6, - blaze-markup >= 0.5.1 && < 0.6 - else - build-depends: - blaze-html >= 0.4.3.0 && < 0.5 - if impl(ghc >= 6.10) - Build-depends: base >= 4 && < 5, syb >= 0.1 && < 0.4 - else - Build-depends: base >= 3 && < 4 + HTTP >= 4000.0.5 && < 4000.3, + citeproc-hs >= 0.3.6 && < 0.4 if impl(ghc >= 7.0.1) - Ghc-Options: -O2 -rtsopts -threaded -Wall -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -rtsopts -Wall -fno-warn-unused-do-bind else if impl(ghc >= 6.12) - Ghc-Options: -O2 -Wall -threaded -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -Wall -fno-warn-unused-do-bind else - Ghc-Options: -O2 -Wall -threaded + Ghc-Options: -Wall if impl(ghc >= 7.0.1) Ghc-Prof-Options: -auto-all -caf-all -rtsopts else Ghc-Prof-Options: -auto-all -caf-all Default-Language: Haskell98 Default-Extensions: CPP - Other-Extensions: PatternGuards, OverloadedStrings, - ScopedTypeVariables, GeneralizedNewtypeDeriving, - RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances, - FlexibleInstances - Hs-Source-Dirs: src - -- END DUPLICATED SECTION - - Main-Is: pandoc.hs - if flag(executable) - Buildable: True - else - Buildable: False + Other-Extensions: PatternGuards, OverloadedStrings, + ScopedTypeVariables, GeneralizedNewtypeDeriving, + RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances, + FlexibleInstances + Hs-Source-Dirs: . + Main-Is: pandoc.hs + Buildable: True -- NOTE: A trick in Setup.hs makes sure this won't be installed: Executable make-pandoc-man-pages - Main-Is: make-pandoc-man-pages.hs + Main-Is: make-pandoc-man-pages.hs Hs-Source-Dirs: man - Build-Depends: base >= 4.2 && < 5, - pandoc, + Build-Depends: pandoc, + base >= 4.2 && < 5, directory >= 1 && < 1.3, filepath >= 1.1 && < 1.4, old-time >= 1.1 && < 1.2, @@ -382,54 +366,64 @@ Test-Suite test-pandoc Type: exitcode-stdio-1.0 Main-Is: test-pandoc.hs Hs-Source-Dirs: tests - if impl(ghc >= 6.10) - Build-depends: base >= 4 && < 5, syb >= 0.1 && < 0.4 - else - Build-depends: base >= 3 && < 4 + Build-Depends: base >= 4.2 && < 5, + syb >= 0.1 && < 0.4, + pandoc, + pandoc-types >= 1.10 && < 1.11, + bytestring >= 0.9 && < 0.11, + text >= 0.11 && < 0.12, + directory >= 1 && < 1.3, + filepath >= 1.1 && < 1.4, + process >= 1 && < 1.2, + Diff >= 0.2 && < 0.3, + test-framework >= 0.3 && < 0.9, + test-framework-hunit >= 0.2 && < 0.4, + test-framework-quickcheck2 >= 0.2.9 && < 0.4, + QuickCheck >= 2.4 && < 2.6, + HUnit >= 1.2 && < 1.3, + template-haskell >= 2.4 && < 2.9, + containers >= 0.1 && < 0.6, + ansi-terminal == 0.5.* + Other-Modules: Tests.Old + Tests.Helpers + Tests.Arbitrary + Tests.Shared + Tests.Readers.LaTeX + Tests.Readers.Markdown + Tests.Readers.RST + Tests.Writers.Native + Tests.Writers.ConTeXt + Tests.Writers.HTML + Tests.Writers.Markdown + Tests.Writers.LaTeX if impl(ghc >= 7.0.1) - Ghc-Options: -O2 -rtsopts -threaded -Wall -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -rtsopts -Wall -fno-warn-unused-do-bind else if impl(ghc >= 6.12) - Ghc-Options: -O2 -Wall -threaded -fno-warn-unused-do-bind -dno-debug-output + Ghc-Options: -Wall -fno-warn-unused-do-bind else - Ghc-Options: -O2 -Wall -threaded - if impl(ghc >= 7.0.1) - Ghc-Prof-Options: -auto-all -caf-all -rtsopts + Ghc-Options: -Wall + if impl(ghc >= 7) + cpp-options: -D_LIT=lit else - Ghc-Prof-Options: -auto-all -caf-all + cpp-options: -D_LIT=$lit Default-Language: Haskell98 - Default-Extensions: CPP - Other-Extensions: PatternGuards, OverloadedStrings, - ScopedTypeVariables, GeneralizedNewtypeDeriving, - RelaxedPolyRec, DeriveDataTypeable, TypeSynonymInstances, - FlexibleInstances - if impl(ghc >= 7) - cpp-options: -D_LIT=lit + Default-Extensions: CPP, TemplateHaskell, QuasiQuotes + +benchmark benchmark-pandoc + Type: exitcode-stdio-1.0 + Main-Is: benchmark-pandoc.hs + Hs-Source-Dirs: benchmark + Build-Depends: pandoc, + base >= 4.2 && < 5, + syb >= 0.1 && < 0.4, + criterion >= 0.5 && < 0.7, + json >= 0.4 && < 0.8 + if impl(ghc >= 7.0.1) + Ghc-Options: -rtsopts -Wall -fno-warn-unused-do-bind else - cpp-options: -D_LIT=$lit - Other-Extensions: TemplateHaskell, QuasiQuotes - Build-Depends: pandoc, Diff, test-framework >= 0.3 && < 0.7, - pandoc-types >= 1.9.0.2 && < 1.10, - test-framework-hunit >= 0.2 && < 0.3, - test-framework-quickcheck2 >= 0.2.9 && < 0.3, - process >= 1 && < 1.2, - filepath >= 1.1 && < 1.4, - directory >= 1 && < 1.3, - bytestring >= 0.9 && < 1.0, - utf8-string >= 0.3 && < 0.4, - QuickCheck >= 2.4 && < 2.6, - HUnit >= 1.2 && < 1.3, - template-haskell >= 2.4 && < 2.9, - ansi-terminal == 0.5.* - Other-Modules: Tests.Old - Tests.Helpers - Tests.Arbitrary - Tests.Shared - Tests.Readers.LaTeX - Tests.Readers.Markdown - Tests.Readers.RST - Tests.Writers.Native - Tests.Writers.ConTeXt - Tests.Writers.HTML - Tests.Writers.Markdown - Tests.Writers.LaTeX + if impl(ghc >= 6.12) + Ghc-Options: -Wall -fno-warn-unused-do-bind + else + Ghc-Options: -Wall + Default-Language: Haskell98 diff --git a/src/pandoc.hs b/pandoc.hs index 77510c906..2b6ed3a46 100644 --- a/src/pandoc.hs +++ b/pandoc.hs @@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} {- -Copyright (C) 2006-2012 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2013 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Main - Copyright : Copyright (C) 2006-2012 John MacFarlane + Copyright : Copyright (C) 2006-2013 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> @@ -29,13 +29,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Parses command-line options and calls the appropriate readers and writers. -} -{-# LANGUAGE ScopedTypeVariables #-} module Main where import Text.Pandoc import Text.Pandoc.PDF (tex2pdf) import Text.Pandoc.Readers.LaTeX (handleIncludes) -import Text.Pandoc.Shared ( tabFilter, ObfuscationMethod (..), readDataFile, - headerShift, findDataFile, normalize, err, warn ) +import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, safeRead, + headerShift, normalize, err, warn ) import Text.Pandoc.XML ( toEntities, fromEntities ) import Text.Pandoc.SelfContained ( makeSelfContained ) import Text.Pandoc.Highlighting ( languages, Style, tango, pygments, @@ -45,38 +44,22 @@ import System.Exit ( exitWith, ExitCode (..) ) import System.FilePath import System.Console.GetOpt import Data.Char ( toLower ) -import Data.List ( intercalate, isSuffixOf, isPrefixOf ) +import Data.List ( intercalate, isPrefixOf ) import System.Directory ( getAppUserDataDirectory, doesFileExist, findExecutable ) import System.IO ( stdout ) import System.IO.Error ( isDoesNotExistError ) +import qualified Control.Exception as E import Control.Exception.Extensible ( throwIO ) import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.CSL as CSL -import Text.Pandoc.Biblio import Control.Monad (when, unless, liftM) import Network.HTTP (simpleHTTP, mkRequest, getResponseBody, RequestMethod(..)) import Network.URI (parseURI, isURI, URI(..)) import qualified Data.ByteString.Lazy as B -import Data.ByteString.Lazy.UTF8 (toString ) import Text.CSL.Reference (Reference(..)) -#if MIN_VERSION_base(4,4,0) -#else -import Codec.Binary.UTF8.String (decodeString, encodeString) -#endif -import qualified Control.Exception as E (catch, IOException) - -encodePath, decodeArg :: FilePath -> FilePath -#if MIN_VERSION_base(4,4,0) -encodePath = id -decodeArg = id -#else -encodePath = encodeString -decodeArg = decodeString -#endif - copyrightMessage :: String -copyrightMessage = "\nCopyright (C) 2006-2012 John MacFarlane\n" ++ +copyrightMessage = "\nCopyright (C) 2006-2013 John MacFarlane\n" ++ "Web: http://johnmacfarlane.net/pandoc\n" ++ "This is free software; see the source for copying conditions. There is no\n" ++ "warranty, not even for merchantability or fitness for a particular purpose." @@ -99,8 +82,8 @@ wrapWords indent c = wrap' (c - indent) (c - indent) then ",\n" ++ replicate indent ' ' ++ x ++ wrap' cols (cols - length x) xs else ", " ++ x ++ wrap' cols (remaining - (length x + 2)) xs -nonTextFormats :: [String] -nonTextFormats = ["odt","docx","epub"] +isTextFormat :: String -> Bool +isTextFormat s = takeWhile (`notElem` "+-") s `notElem` ["odt","docx","epub","epub3"] -- | Data structure for command line options. data Opt = Opt @@ -122,18 +105,20 @@ data Opt = Opt , optSmart :: Bool -- ^ Use smart typography , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML + , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML , optHighlight :: Bool -- ^ Highlight source code , optHighlightStyle :: Style -- ^ Style to use for highlighted code , optChapters :: Bool -- ^ Use chapter for top-level sects , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx - , optEPUBStylesheet :: Maybe String -- ^ EPUB stylesheet - , optEPUBMetadata :: String -- ^ EPUB metadata - , optEPUBFonts :: [FilePath] -- ^ EPUB fonts to embed + , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet + , optEpubMetadata :: String -- ^ EPUB metadata + , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed + , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters + , optTOCDepth :: Int -- ^ Number of levels to include in TOC , optDumpArgs :: Bool -- ^ Output command-line arguments , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments - , optStrict :: Bool -- ^ Use strict markdown syntax , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst , optWrapText :: Bool -- ^ Wrap text , optColumns :: Int -- ^ Line length in characters @@ -144,7 +129,7 @@ data Opt = Opt , optDataDir :: Maybe FilePath , optCiteMethod :: CiteMethod -- ^ Method to output cites , optBibliography :: [String] - , optCslFile :: FilePath + , optCslFile :: Maybe FilePath , optAbbrevsFile :: Maybe FilePath , optListings :: Bool -- ^ Use listings package for code blocks , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf @@ -175,18 +160,20 @@ defaultOpts = Opt , optSmart = False , optOldDashes = False , optHtml5 = False + , optHtmlQTags = False , optHighlight = True , optHighlightStyle = pygments , optChapters = False , optHTMLMathMethod = PlainMath , optReferenceODT = Nothing , optReferenceDocx = Nothing - , optEPUBStylesheet = Nothing - , optEPUBMetadata = "" - , optEPUBFonts = [] + , optEpubStylesheet = Nothing + , optEpubMetadata = "" + , optEpubFonts = [] + , optEpubChapterLevel = 1 + , optTOCDepth = 3 , optDumpArgs = False , optIgnoreArgs = False - , optStrict = False , optReferenceLinks = False , optWrapText = True , optColumns = 72 @@ -197,7 +184,7 @@ defaultOpts = Opt , optDataDir = Nothing , optCiteMethod = Citeproc , optBibliography = [] - , optCslFile = "" + , optCslFile = Nothing , optAbbrevsFile = Nothing , optListings = False , optLaTeXEngine = "pdflatex" @@ -237,7 +224,10 @@ options = , Option "" ["strict"] (NoArg - (\opt -> return opt { optStrict = True } )) + (\opt -> do + err 59 $ "The --strict option has been removed.\n" ++ + "Use `markdown_strict' input or output format instead." + return opt )) "" -- "Disable markdown syntax extensions" , Option "R" ["parse-raw"] @@ -259,13 +249,13 @@ options = , Option "" ["base-header-level"] (ReqArg (\arg opt -> - case reads arg of - [(t,"")] | t > 0 -> do + case safeRead arg of + Just t | t > 0 -> do let oldTransforms = optTransforms opt let shift = t - 1 return opt{ optTransforms = headerShift shift : oldTransforms } - _ -> err 19 + _ -> err 19 "base-header-level must be a number > 0") "NUMBER") "" -- "Headers base level" @@ -291,9 +281,9 @@ options = , Option "" ["tab-stop"] (ReqArg (\arg opt -> - case reads arg of - [(t,"")] | t > 0 -> return opt { optTabStop = t } - _ -> err 31 + case safeRead arg of + Just t | t > 0 -> return opt { optTabStop = t } + _ -> err 31 "tab-stop must be a number greater than 0") "NUMBER") "" -- "Tab stop (default 4)" @@ -317,8 +307,7 @@ options = let (key,val) = case break (`elem` ":=") arg of (k,_:v) -> (k,v) (k,_) -> (k,"true") - let newvars = optVariables opt ++ [(key,val)] - return opt{ optVariables = newvars }) + return opt{ optVariables = (key,val) : optVariables opt }) "KEY[:VALUE]") "" -- "Use custom template" @@ -341,9 +330,9 @@ options = , Option "" ["columns"] (ReqArg (\arg opt -> - case reads arg of - [(t,"")] | t > 0 -> return opt { optColumns = t } - _ -> err 33 $ + case safeRead arg of + Just t | t > 0 -> return opt { optColumns = t } + _ -> err 33 $ "columns must be a number greater than 0") "NUMBER") "" -- "Length of line in characters" @@ -353,6 +342,18 @@ options = (\opt -> return opt { optTableOfContents = True })) "" -- "Include table of contents" + , Option "" ["toc-depth"] + (ReqArg + (\arg opt -> do + case safeRead arg of + Just t | t >= 1 && t <= 6 -> + return opt { optTOCDepth = t, + optTableOfContents = True } + _ -> err 57 $ + "TOC level must be a number between 1 and 6") + "NUMBER") + "" -- "Number of levels to include in TOC" + , Option "" ["no-highlight"] (NoArg (\opt -> return opt { optHighlight = False })) @@ -432,6 +433,12 @@ options = return opt { optHtml5 = True })) "" -- "Produce HTML5 in HTML output" + , Option "" ["html-q-tags"] + (NoArg + (\opt -> do + return opt { optHtmlQTags = True })) + "" -- "Use <q> tags for quotes in HTML" + , Option "" ["ascii"] (NoArg (\opt -> return opt { optAscii = True })) @@ -475,10 +482,10 @@ options = , Option "" ["slide-level"] (ReqArg (\arg opt -> do - case reads arg of - [(t,"")] | t >= 1 && t <= 6 -> + case safeRead arg of + Just t | t >= 1 && t <= 6 -> return opt { optSlideLevel = Just t } - _ -> err 39 $ + _ -> err 39 $ "slide level must be a number between 1 and 6") "NUMBER") "" -- "Force header level for slides" @@ -544,7 +551,7 @@ options = (ReqArg (\arg opt -> do text <- UTF8.readFile arg - return opt { optEPUBStylesheet = Just text }) + return opt { optEpubStylesheet = Just text }) "FILENAME") "" -- "Path of epub.css" @@ -560,17 +567,28 @@ options = (ReqArg (\arg opt -> do text <- UTF8.readFile arg - return opt { optEPUBMetadata = text }) + return opt { optEpubMetadata = text }) "FILENAME") "" -- "Path of epub metadata file" , Option "" ["epub-embed-font"] (ReqArg (\arg opt -> do - return opt{ optEPUBFonts = arg : optEPUBFonts opt }) + return opt{ optEpubFonts = arg : optEpubFonts opt }) "FILE") "" -- "Directory of fonts to embed" + , Option "" ["epub-chapter-level"] + (ReqArg + (\arg opt -> do + case safeRead arg of + Just t | t >= 1 && t <= 6 -> + return opt { optEpubChapterLevel = t } + _ -> err 59 $ + "chapter level must be a number between 1 and 6") + "NUMBER") + "" -- "Header level at which to split chapters in EPUB" + , Option "" ["latex-engine"] (ReqArg (\arg opt -> do @@ -589,7 +607,7 @@ options = , Option "" ["csl"] (ReqArg - (\arg opt -> return opt { optCslFile = arg }) + (\arg opt -> return opt { optCslFile = Just arg }) "FILENAME") "" @@ -654,7 +672,7 @@ options = (\arg opt -> do let url' = case arg of Just u -> u - Nothing -> "https://d3eoax9i5htok0.cloudfront.net/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" + Nothing -> "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" return opt { optHTMLMathMethod = MathJax url'}) "URL") "" -- "Use MathJax for HTML math" @@ -697,8 +715,11 @@ options = usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++ - (wrapWords 16 78 $ map fst readers) ++ "\nOutput formats: " ++ - (wrapWords 16 78 $ map fst writers ++ nonTextFormats) ++ "\nOptions:") + (wrapWords 16 78 $ readers'names) ++ "\nOutput formats: " ++ + (wrapWords 16 78 $ writers'names) ++ "\nOptions:") + where + writers'names = map fst writers + readers'names = map fst readers -- Determine default reader based on source file extensions defaultReaderName :: String -> [FilePath] -> String @@ -714,6 +735,7 @@ defaultReaderName fallback (x:xs) = ".rst" -> "rst" ".lhs" -> "markdown+lhs" ".db" -> "docbook" + ".wiki" -> "mediawiki" ".textile" -> "textile" ".native" -> "native" ".json" -> "json" @@ -755,13 +777,14 @@ defaultWriterName x = ".org" -> "org" ".asciidoc" -> "asciidoc" ".pdf" -> "latex" + ".fb2" -> "fb2" ['.',y] | y `elem` ['1'..'9'] -> "man" _ -> "html" main :: IO () main = do - rawArgs <- liftM (map decodeArg) getArgs + rawArgs <- liftM (map UTF8.decodeArg) getArgs prg <- getProgName let compatMode = (prg == "hsmarkdown") @@ -774,9 +797,10 @@ main = do ["Try " ++ prg ++ " --help for more information."] let defaultOpts' = if compatMode - then defaultOpts { optReader = "markdown" + then defaultOpts { optReader = "markdown_strict" , optWriter = "html" - , optStrict = True } + , optEmailObfuscation = + ReferenceObfuscation } else defaultOpts -- thread option data structure through all supplied option actions @@ -800,18 +824,20 @@ main = do , optSmart = smart , optOldDashes = oldDashes , optHtml5 = html5 + , optHtmlQTags = htmlQTags , optHighlight = highlight , optHighlightStyle = highlightStyle , optChapters = chapters , optHTMLMathMethod = mathMethod , optReferenceODT = referenceODT , optReferenceDocx = referenceDocx - , optEPUBStylesheet = epubStylesheet - , optEPUBMetadata = epubMetadata - , optEPUBFonts = epubFonts + , optEpubStylesheet = epubStylesheet + , optEpubMetadata = epubMetadata + , optEpubFonts = epubFonts + , optEpubChapterLevel = epubChapterLevel + , optTOCDepth = epubTOCDepth , optDumpArgs = dumpArgs , optIgnoreArgs = ignoreArgs - , optStrict = strict , optReferenceLinks = referenceLinks , optWrapText = wrap , optColumns = columns @@ -820,7 +846,7 @@ main = do , optIndentedCodeClasses = codeBlockClasses , optDataDir = mbDataDir , optBibliography = reffiles - , optCslFile = cslfile + , optCslFile = mbCsl , optAbbrevsFile = cslabbrevs , optCiteMethod = citeMethod , optListings = listings @@ -841,7 +867,8 @@ main = do datadir <- case mbDataDir of Nothing -> E.catch (liftM Just $ getAppUserDataDirectory "pandoc") - (\(_::E.IOException) -> return Nothing) + (\e -> let _ = (e :: E.SomeException) + in return Nothing) Just _ -> return mbDataDir -- assign reader and writer based on options and filenames @@ -858,8 +885,8 @@ main = do let pdfOutput = map toLower (takeExtension outputFile) == ".pdf" - let laTeXOutput = writerName' == "latex" || writerName' == "beamer" || - writerName' == "latex+lhs" || writerName' == "beamer+lhs" + let laTeXOutput = "latex" `isPrefixOf` writerName' || + "beamer" `isPrefixOf` writerName' when pdfOutput $ do -- make sure writer is latex or beamer @@ -873,11 +900,11 @@ main = do latexEngine ++ " is needed for pdf output." Just _ -> return () - reader <- case (lookup readerName' readers) of - Just r -> return r - Nothing -> err 7 ("Unknown reader: " ++ readerName') + reader <- case getReader readerName' of + Right r -> return r + Left e -> err 7 e - let standalone' = standalone || writerName' `elem` nonTextFormats || pdfOutput + let standalone' = standalone || not (isTextFormat writerName') || pdfOutput templ <- case templatePath of _ | not standalone' -> return "" @@ -887,112 +914,129 @@ main = do Left e -> throwIO e Right t -> return t Just tp -> do - -- strip off "+lhs" if present - let format = takeWhile (/='+') writerName' + -- strip off extensions + let format = takeWhile (`notElem` "+-") writerName' let tp' = case takeExtension tp of "" -> tp <.> format _ -> tp E.catch (UTF8.readFile tp') - (\(e::E.IOException) -> if isDoesNotExistError e + (\e -> if isDoesNotExistError e then E.catch - (readDataFile datadir $ - "templates" </> tp') - (\(_::E.IOException) -> throwIO e) + (readDataFileUTF8 datadir + ("templates" </> tp')) + (\e' -> let _ = (e' :: E.SomeException) + in throwIO e') else throwIO e) - let slideVariant = case writerName' of - "s5" -> S5Slides - "slidy" -> SlidySlides - "slideous" -> SlideousSlides - "dzslides" -> DZSlides - _ -> NoSlides - variables' <- case mathMethod of LaTeXMathML Nothing -> do - s <- readDataFile datadir $ "data" </> "LaTeXMathML.js" + s <- readDataFileUTF8 datadir + ("LaTeXMathML.js") return $ ("mathml-script", s) : variables MathML Nothing -> do - s <- readDataFile datadir $ "data"</>"MathMLinHTML.js" + s <- readDataFileUTF8 datadir + ("MathMLinHTML.js") return $ ("mathml-script", s) : variables _ -> return variables - variables'' <- case slideVariant of - DZSlides -> do - dztempl <- readDataFile datadir $ "dzslides" </> "template.html" + variables'' <- if "dzslides" `isPrefixOf` writerName' + then do + dztempl <- readDataFileUTF8 datadir + ("dzslides" </> "template.html") let dzcore = unlines $ dropWhile (not . isPrefixOf "<!-- {{{{ dzslides core") $ lines dztempl return $ ("dzslides-core", dzcore) : variables' - _ -> return variables' + else return variables' -- unescape reference ids, which may contain XML entities, so -- that we can do lookups with regular string equality let unescapeRefId ref = ref{ refId = fromEntities (refId ref) } - refs <- mapM (\f -> E.catch (CSL.readBiblioFile f) $ \(e::E.IOException) -> - err 23 $ "Error reading bibliography `" ++ f ++ "'" ++ "\n" ++ show e) + refs <- mapM (\f -> E.catch (CSL.readBiblioFile f) + (\e -> let _ = (e :: E.SomeException) + in err 23 $ "Error reading bibliography `" ++ f ++ + "'" ++ "\n" ++ show e)) reffiles >>= return . map unescapeRefId . concat - let sourceDir = if null sources - then "." - else takeDirectory (head sources) - - let startParserState = - defaultParserState { stateParseRaw = parseRaw, - stateTabStop = tabStop, - stateLiterateHaskell = "+lhs" `isSuffixOf` readerName' || - lhsExtension sources, - stateStandalone = standalone', - stateCitations = map CSL.refId refs, - stateSmart = smart || (texLigatures && - (laTeXOutput || writerName' == "context")), - stateOldDashes = oldDashes, - stateColumns = columns, - stateStrict = strict, - stateIndentedCodeClasses = codeBlockClasses, - stateApplyMacros = not laTeXOutput - } - - let writerOptions = defaultWriterOptions - { writerStandalone = standalone', - writerTemplate = templ, - writerVariables = variables'', - writerEPUBMetadata = epubMetadata, - writerTabStop = tabStop, - writerTableOfContents = toc && - writerName' /= "s5", - writerHTMLMathMethod = mathMethod, - writerSlideVariant = slideVariant, - writerIncremental = incremental, - writerCiteMethod = citeMethod, - writerBiblioFiles = reffiles, - writerIgnoreNotes = False, - writerNumberSections = numberSections, - writerSectionDivs = sectionDivs, - writerStrictMarkdown = strict, - writerReferenceLinks = referenceLinks, - writerWrapText = wrap, - writerColumns = columns, - writerLiterateHaskell = False, - writerEmailObfuscation = if strict - then ReferenceObfuscation - else obfuscationMethod, - writerIdentifierPrefix = idPrefix, - writerSourceDirectory = sourceDir, - writerUserDataDir = datadir, - writerHtml5 = html5 || - slideVariant == DZSlides, - writerChapters = chapters, - writerListings = listings, - writerBeamer = False, - writerSlideLevel = slideLevel, - writerHighlight = highlight, - writerHighlightStyle = highlightStyle, - writerSetextHeaders = setextHeaders, - writerTeXLigatures = texLigatures - } - - when (writerName' `elem` nonTextFormats&& outputFile == "-") $ + mbsty <- if citeMethod == Citeproc && not (null refs) + then do + csl <- CSL.parseCSL =<< + case mbCsl of + Nothing -> readDataFileUTF8 datadir + "default.csl" + Just cslfile -> do + exists <- doesFileExist cslfile + if exists + then UTF8.readFile cslfile + else do + csldir <- getAppUserDataDirectory "csl" + readDataFileUTF8 (Just csldir) + (replaceExtension cslfile "csl") + abbrevs <- maybe (return []) CSL.readJsonAbbrevFile cslabbrevs + return $ Just csl { CSL.styleAbbrevs = abbrevs } + else return Nothing + + let sourceDir = case sources of + [] -> "." + (x:_) -> case parseURI x of + Just u + | uriScheme u `elem` ["http:","https:"] -> + show u{ uriPath = "", uriQuery = "", uriFragment = "" } + _ -> takeDirectory x + + let readerOpts = def{ readerSmart = smart || (texLigatures && + (laTeXOutput || "context" `isPrefixOf` writerName')) + , readerStandalone = standalone' + , readerParseRaw = parseRaw + , readerColumns = columns + , readerTabStop = tabStop + , readerOldDashes = oldDashes + , readerReferences = refs + , readerCitationStyle = mbsty + , readerIndentedCodeClasses = codeBlockClasses + , readerApplyMacros = not laTeXOutput + } + + let writerOptions = def { writerStandalone = standalone', + writerTemplate = templ, + writerVariables = variables'', + writerTabStop = tabStop, + writerTableOfContents = toc, + writerHTMLMathMethod = mathMethod, + writerIncremental = incremental, + writerCiteMethod = citeMethod, + writerBiblioFiles = reffiles, + writerIgnoreNotes = False, + writerNumberSections = numberSections, + writerSectionDivs = sectionDivs, + writerReferenceLinks = referenceLinks, + writerWrapText = wrap, + writerColumns = columns, + writerEmailObfuscation = obfuscationMethod, + writerIdentifierPrefix = idPrefix, + writerSourceDirectory = sourceDir, + writerUserDataDir = datadir, + writerHtml5 = html5, + writerHtmlQTags = htmlQTags, + writerChapters = chapters, + writerListings = listings, + writerBeamer = False, + writerSlideLevel = slideLevel, + writerHighlight = highlight, + writerHighlightStyle = highlightStyle, + writerSetextHeaders = setextHeaders, + writerTeXLigatures = texLigatures, + writerEpubMetadata = epubMetadata, + writerEpubStylesheet = epubStylesheet, + writerEpubFonts = epubFonts, + writerEpubChapterLevel = epubChapterLevel, + writerTOCDepth = epubTOCDepth, + writerReferenceODT = referenceODT, + writerReferenceDocx = referenceDocx + } + + when (not (isTextFormat writerName') && outputFile == "-") $ err 5 $ "Cannot write " ++ writerName' ++ " output to stdout.\n" ++ "Specify an output file using the -o option." @@ -1004,7 +1048,7 @@ main = do readURI u _ -> UTF8.readFile src readURI uri = simpleHTTP (mkRequest GET uri) >>= getResponseBody >>= - return . toString -- treat all as UTF8 + return . UTF8.toStringLazy -- treat all as UTF8 let convertTabs = tabFilter (if preserveTabs then 0 else tabStop) @@ -1012,64 +1056,38 @@ main = do then handleIncludes else return - doc <- (reader startParserState) `fmap` (readSources sources >>= - handleIncludes' . convertTabs . intercalate "\n") + doc <- readSources sources >>= + handleIncludes' . convertTabs . intercalate "\n" >>= + reader readerOpts let doc0 = foldr ($) doc transforms - doc1 <- if writerName' == "rtf" - then bottomUpM rtfEmbedImage doc0 - else return doc0 - - doc2 <- do - if citeMethod == Citeproc && not (null refs) - then do - csldir <- getAppUserDataDirectory "csl" - cslfile' <- if null cslfile - then findDataFile datadir "default.csl" - else do - ex <- doesFileExist cslfile - if ex - then return cslfile - else findDataFile datadir $ - replaceDirectory - (replaceExtension cslfile "csl") - csldir - processBiblio cslfile' cslabbrevs refs doc1 - else return doc1 - let writeBinary :: B.ByteString -> IO () - writeBinary = B.writeFile (encodePath outputFile) + writeBinary = B.writeFile (UTF8.encodePath outputFile) let writerFn :: FilePath -> String -> IO () writerFn "-" = UTF8.putStr writerFn f = UTF8.writeFile f - case lookup writerName' writers of - Nothing - | writerName' == "epub" -> - writeEPUB epubStylesheet epubFonts writerOptions doc2 - >>= writeBinary - | writerName' == "odt" -> - writeODT referenceODT writerOptions doc2 >>= writeBinary - | writerName' == "docx" -> - writeDocx referenceDocx writerOptions doc2 >>= writeBinary - | otherwise -> err 9 ("Unknown writer: " ++ writerName') - Just w - | pdfOutput -> do - res <- tex2pdf latexEngine $ w writerOptions doc2 + case getWriter writerName' of + Left e -> err 9 e + Right (IOStringWriter f) -> f writerOptions doc0 >>= writerFn outputFile + Right (IOByteStringWriter f) -> f writerOptions doc0 >>= writeBinary + Right (PureStringWriter f) + | pdfOutput -> do + res <- tex2pdf latexEngine $ f writerOptions doc0 case res of Right pdf -> writeBinary pdf - Left err' -> err 43 $ toString err' - Just w - | htmlFormat && ascii -> - writerFn outputFile =<< selfcontain (toEntities result) - | otherwise -> - writerFn outputFile =<< selfcontain result - where result = w writerOptions doc2 ++ ['\n' | not standalone'] - htmlFormat = writerName' `elem` + Left err' -> err 43 $ UTF8.toStringLazy err' + | otherwise -> selfcontain (f writerOptions doc0 ++ + ['\n' | not standalone']) + >>= writerFn outputFile . handleEntities + where htmlFormat = writerName' `elem` ["html","html+lhs","html5","html5+lhs", "s5","slidy","slideous","dzslides"] selfcontain = if selfContained && htmlFormat then makeSelfContained datadir else return + handleEntities = if htmlFormat && ascii + then toEntities + else id diff --git a/reference.docx b/reference.docx Binary files differdeleted file mode 100644 index c321408d1..000000000 --- a/reference.docx +++ /dev/null diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 432a5c2ba..4301ad49a 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -20,10 +20,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha + Stability : alpha Portability : portable This helper module exports the main writers, readers, and data @@ -43,9 +43,8 @@ inline links: > > markdownToRST :: String -> String > markdownToRST = -> (writeRST defaultWriterOptions {writerReferenceLinks = True}) . -> readMarkdown defaultParserState -> +> (writeRST def {writerReferenceLinks = True}) . readMarkdown def +> > main = getContents >>= putStrLn . markdownToRST Note: all of the readers assume that the input text has @'\n'@ @@ -55,31 +54,27 @@ you should remove @'\r'@ characters using @filter (/='\r')@. -} module Text.Pandoc - ( + ( -- * Definitions module Text.Pandoc.Definition -- * Generics , module Text.Pandoc.Generic + -- * Options + , module Text.Pandoc.Options -- * Lists of readers and writers , readers , writers -- * Readers: converting /to/ Pandoc format , readMarkdown + , readMediaWiki , readRST , readLaTeX , readHtml , readTextile , readDocBook , readNative - -- * Parser state used in readers - , ParserState (..) - , defaultParserState - , ParserContext (..) - , QuoteContext (..) - , KeyTable - , NoteTable - , HeaderType (..) -- * Writers: converting /from/ Pandoc format + , Writer (..) , writeNative , writeMarkdown , writePlain @@ -98,20 +93,16 @@ module Text.Pandoc , writeODT , writeDocx , writeEPUB + , writeFB2 , writeOrg , writeAsciiDoc - -- * Writer options used in writers - , WriterOptions (..) - , HTMLSlideVariant (..) - , HTMLMathMethod (..) - , CiteMethod (..) - , defaultWriterOptions -- * Rendering templates and default templates , module Text.Pandoc.Templates -- * Version , pandocVersion -- * Miscellaneous - , rtfEmbedImage + , getReader + , getWriter , jsonFilter , ToJsonFilter(..) ) where @@ -119,6 +110,7 @@ module Text.Pandoc import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.Readers.Markdown +import Text.Pandoc.Readers.MediaWiki import Text.Pandoc.Readers.RST import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.LaTeX @@ -127,7 +119,7 @@ import Text.Pandoc.Readers.Textile import Text.Pandoc.Readers.Native import Text.Pandoc.Writers.Native import Text.Pandoc.Writers.Markdown -import Text.Pandoc.Writers.RST +import Text.Pandoc.Writers.RST import Text.Pandoc.Writers.LaTeX import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.Texinfo @@ -135,85 +127,162 @@ import Text.Pandoc.Writers.HTML import Text.Pandoc.Writers.ODT import Text.Pandoc.Writers.Docx import Text.Pandoc.Writers.EPUB +import Text.Pandoc.Writers.FB2 import Text.Pandoc.Writers.Docbook import Text.Pandoc.Writers.OpenDocument import Text.Pandoc.Writers.Man -import Text.Pandoc.Writers.RTF +import Text.Pandoc.Writers.RTF import Text.Pandoc.Writers.MediaWiki import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.Org import Text.Pandoc.Writers.AsciiDoc import Text.Pandoc.Templates -import Text.Pandoc.Parsing -import Text.Pandoc.Shared +import Text.Pandoc.Options +import Text.Pandoc.Shared (safeRead, warn) +import Data.ByteString.Lazy (ByteString) +import Data.List (intercalate) import Data.Version (showVersion) import Text.JSON.Generic +import Data.Set (Set) +import qualified Data.Set as Set +import Text.Parsec +import Text.Parsec.Error import Paths_pandoc (version) -- | Version number of pandoc library. pandocVersion :: String pandocVersion = showVersion version +parseFormatSpec :: String + -> Either ParseError (String, Set Extension -> Set Extension) +parseFormatSpec = parse formatSpec "" + where formatSpec = do + name <- formatName + extMods <- many extMod + return (name, foldl (.) id extMods) + formatName = many1 $ noneOf "-+" + extMod = do + polarity <- oneOf "-+" + name <- many $ noneOf "-+" + ext <- case safeRead ("Ext_" ++ name) of + Just n -> return n + Nothing + | name == "lhs" -> return Ext_literate_haskell + | otherwise -> fail $ "Unknown extension: " ++ name + return $ case polarity of + '-' -> Set.delete ext + _ -> Set.insert ext + +-- auxiliary function for readers: +markdown :: ReaderOptions -> String -> IO Pandoc +markdown o s = do + let (doc, warnings) = readMarkdownWithWarnings o s + mapM_ warn warnings + return doc + -- | Association list of formats and readers. -readers :: [(String, ParserState -> String -> Pandoc)] -readers = [("native" , \_ -> readNative) - ,("json" , \_ -> decodeJSON) - ,("markdown" , readMarkdown) - ,("markdown+lhs" , \st -> - readMarkdown st{ stateLiterateHaskell = True}) - ,("rst" , readRST) - ,("rst+lhs" , \st -> - readRST st{ stateLiterateHaskell = True}) - ,("docbook" , readDocBook) - ,("textile" , readTextile) -- TODO : textile+lhs - ,("html" , readHtml) - ,("latex" , readLaTeX) - ,("latex+lhs" , \st -> - readLaTeX st{ stateLiterateHaskell = True}) +readers :: [(String, ReaderOptions -> String -> IO Pandoc)] +readers = [("native" , \_ s -> return $ readNative s) + ,("json" , \_ s -> return $ decodeJSON s) + ,("markdown" , markdown) + ,("markdown_strict" , markdown) + ,("markdown_phpextra" , markdown) + ,("markdown_mmd", markdown) + ,("rst" , \o s -> return $ readRST o s) + ,("mediawiki" , \o s -> return $ readMediaWiki o s) + ,("docbook" , \o s -> return $ readDocBook o s) + ,("textile" , \o s -> return $ readTextile o s) -- TODO : textile+lhs + ,("html" , \o s -> return $ readHtml o s) + ,("latex" , \o s -> return $ readLaTeX o s) ] --- | Association list of formats and writers (omitting the --- binary writers, odt, docx, and epub). -writers :: [ ( String, WriterOptions -> Pandoc -> String ) ] -writers = [("native" , writeNative) - ,("json" , \_ -> encodeJSON) - ,("html" , writeHtmlString) - ,("html5" , \o -> - writeHtmlString o{ writerHtml5 = True }) - ,("html+lhs" , \o -> - writeHtmlString o{ writerLiterateHaskell = True }) - ,("html5+lhs" , \o -> - writeHtmlString o{ writerLiterateHaskell = True, - writerHtml5 = True }) - ,("s5" , writeHtmlString) - ,("slidy" , writeHtmlString) - ,("slideous" , writeHtmlString) - ,("dzslides" , writeHtmlString) - ,("docbook" , writeDocbook) - ,("opendocument" , writeOpenDocument) - ,("latex" , writeLaTeX) - ,("latex+lhs" , \o -> - writeLaTeX o{ writerLiterateHaskell = True }) - ,("beamer" , \o -> - writeLaTeX o{ writerBeamer = True }) - ,("beamer+lhs" , \o -> - writeLaTeX o{ writerBeamer = True, writerLiterateHaskell = True }) - ,("context" , writeConTeXt) - ,("texinfo" , writeTexinfo) - ,("man" , writeMan) - ,("markdown" , writeMarkdown) - ,("markdown+lhs" , \o -> - writeMarkdown o{ writerLiterateHaskell = True }) - ,("plain" , writePlain) - ,("rst" , writeRST) - ,("rst+lhs" , \o -> - writeRST o{ writerLiterateHaskell = True }) - ,("mediawiki" , writeMediaWiki) - ,("textile" , writeTextile) - ,("rtf" , writeRTF) - ,("org" , writeOrg) - ,("asciidoc" , writeAsciiDoc) - ] +data Writer = PureStringWriter (WriterOptions -> Pandoc -> String) + | IOStringWriter (WriterOptions -> Pandoc -> IO String) + | IOByteStringWriter (WriterOptions -> Pandoc -> IO ByteString) + +-- | Association list of formats and writers. +writers :: [ ( String, Writer ) ] +writers = [ + ("native" , PureStringWriter writeNative) + ,("json" , PureStringWriter $ \_ -> encodeJSON) + ,("docx" , IOByteStringWriter writeDocx) + ,("odt" , IOByteStringWriter writeODT) + ,("epub" , IOByteStringWriter $ \o -> + writeEPUB o{ writerEpubVersion = Just EPUB2 }) + ,("epub3" , IOByteStringWriter $ \o -> + writeEPUB o{ writerEpubVersion = Just EPUB3 }) + ,("fb2" , IOStringWriter writeFB2) + ,("html" , PureStringWriter writeHtmlString) + ,("html5" , PureStringWriter $ \o -> + writeHtmlString o{ writerHtml5 = True }) + ,("s5" , PureStringWriter $ \o -> + writeHtmlString o{ writerSlideVariant = S5Slides + , writerTableOfContents = False }) + ,("slidy" , PureStringWriter $ \o -> + writeHtmlString o{ writerSlideVariant = SlidySlides }) + ,("slideous" , PureStringWriter $ \o -> + writeHtmlString o{ writerSlideVariant = SlideousSlides }) + ,("dzslides" , PureStringWriter $ \o -> + writeHtmlString o{ writerSlideVariant = DZSlides + , writerHtml5 = True }) + ,("docbook" , PureStringWriter writeDocbook) + ,("opendocument" , PureStringWriter writeOpenDocument) + ,("latex" , PureStringWriter writeLaTeX) + ,("beamer" , PureStringWriter $ \o -> + writeLaTeX o{ writerBeamer = True }) + ,("context" , PureStringWriter writeConTeXt) + ,("texinfo" , PureStringWriter writeTexinfo) + ,("man" , PureStringWriter writeMan) + ,("markdown" , PureStringWriter writeMarkdown) + ,("markdown_strict" , PureStringWriter writeMarkdown) + ,("markdown_phpextra" , PureStringWriter writeMarkdown) + ,("markdown_github" , PureStringWriter writeMarkdown) + ,("markdown_mmd" , PureStringWriter writeMarkdown) + ,("plain" , PureStringWriter writePlain) + ,("rst" , PureStringWriter writeRST) + ,("mediawiki" , PureStringWriter writeMediaWiki) + ,("textile" , PureStringWriter writeTextile) + ,("rtf" , IOStringWriter writeRTFWithEmbeddedImages) + ,("org" , PureStringWriter writeOrg) + ,("asciidoc" , PureStringWriter writeAsciiDoc) + ] + +getDefaultExtensions :: String -> Set Extension +getDefaultExtensions "markdown_strict" = strictExtensions +getDefaultExtensions "markdown_phpextra" = phpMarkdownExtraExtensions +getDefaultExtensions "markdown_mmd" = multimarkdownExtensions +getDefaultExtensions "markdown_github" = githubMarkdownExtensions +getDefaultExtensions _ = pandocExtensions + +-- | Retrieve reader based on formatSpec (format+extensions). +getReader :: String -> Either String (ReaderOptions -> String -> IO Pandoc) +getReader s = + case parseFormatSpec s of + Left e -> Left $ intercalate "\n" $ [m | Message m <- errorMessages e] + Right (readerName, setExts) -> + case lookup readerName readers of + Nothing -> Left $ "Unknown reader: " ++ readerName + Just r -> Right $ \o -> + r o{ readerExtensions = setExts $ + getDefaultExtensions readerName } + +-- | Retrieve writer based on formatSpec (format+extensions). +getWriter :: String -> Either String Writer +getWriter s = + case parseFormatSpec s of + Left e -> Left $ intercalate "\n" $ [m | Message m <- errorMessages e] + Right (writerName, setExts) -> + case lookup writerName writers of + Nothing -> Left $ "Unknown writer: " ++ writerName + Just (PureStringWriter r) -> Right $ PureStringWriter $ + \o -> r o{ writerExtensions = setExts $ + getDefaultExtensions writerName } + Just (IOStringWriter r) -> Right $ IOStringWriter $ + \o -> r o{ writerExtensions = setExts $ + getDefaultExtensions writerName } + Just (IOByteStringWriter r) -> Right $ IOByteStringWriter $ + \o -> r o{ writerExtensions = setExts $ + getDefaultExtensions writerName } {-# DEPRECATED jsonFilter "Use toJsonFilter instead" #-} -- | Converts a transformation on the Pandoc AST into a function diff --git a/src/Text/Pandoc/Biblio.hs b/src/Text/Pandoc/Biblio.hs index cece13fba..4dd82dd08 100644 --- a/src/Text/Pandoc/Biblio.hs +++ b/src/Text/Pandoc/Biblio.hs @@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA module Text.Pandoc.Biblio ( processBiblio ) where import Data.List -import Data.Unique import Data.Char ( isDigit, isPunctuation ) import qualified Data.Map as M import Text.CSL hiding ( Cite(..), Citation(..) ) @@ -38,30 +37,24 @@ import qualified Text.CSL as CSL ( Cite(..) ) import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.Shared (stringify) -import Text.ParserCombinators.Parsec +import Text.Parsec hiding (State) import Control.Monad +import Control.Monad.State -- | Process a 'Pandoc' document by adding citations formatted -- according to a CSL style, using 'citeproc' from citeproc-hs. -processBiblio :: FilePath -> Maybe FilePath -> [Reference] -> Pandoc - -> IO Pandoc -processBiblio cslfile abrfile r p - = if null r then return p - else do - csl <- readCSLFile cslfile - abbrevs <- case abrfile of - Just f -> readJsonAbbrevFile f - Nothing -> return [] - p' <- bottomUpM setHash p - let grps = queryWith getCitation p' - style = csl { styleAbbrevs = abbrevs } - result = citeproc procOpts style r (setNearNote style $ - map (map toCslCite) grps) - cits_map = M.fromList $ zip grps (citations result) - biblioList = map (renderPandoc' style) (bibliography result) - Pandoc m b = bottomUp (processCite style cits_map) p' - b' = bottomUp mvPunct $ deNote b - return $ Pandoc m $ b' ++ biblioList +processBiblio :: Maybe Style -> [Reference] -> Pandoc -> Pandoc +processBiblio Nothing _ p = p +processBiblio _ [] p = p +processBiblio (Just style) r p = + let p' = evalState (bottomUpM setHash p) 1 + grps = queryWith getCitation p' + result = citeproc procOpts style r (setNearNote style $ + map (map toCslCite) grps) + cits_map = M.fromList $ zip grps (citations result) + biblioList = map (renderPandoc' style) (bibliography result) + Pandoc m b = bottomUp mvPunct . deNote . bottomUp (processCite style cits_map) $ p' + in Pandoc m $ b ++ biblioList -- | Substitute 'Cite' elements with formatted citations. processCite :: Style -> M.Map [Citation] [FormattedOutput] -> Inline -> Inline @@ -92,18 +85,10 @@ mvPunct (Space : x : ys) | isNote x = x : ys mvPunct xs = xs sanitize :: [Inline] -> [Inline] -sanitize xs | endWithPunct xs = toCapital' xs - | otherwise = toCapital' (xs ++ [Str "."]) - --- NOTE: toCapital' works around a bug in toCapital from citeproc-hs 0.3.4. --- When citeproc-hs is fixed, we can return to using toCapital in sanitize. -toCapital' :: [Inline] -> [Inline] -toCapital' [] = [] -toCapital' xs = case toCapital xs of - [] -> xs - ys -> ys - -deNote :: [Block] -> [Block] +sanitize xs | endWithPunct xs = toCapital xs + | otherwise = toCapital (xs ++ [Str "."]) + +deNote :: Pandoc -> Pandoc deNote = topDown go where go (Note [Para xs]) = Note $ bottomUp go' [Para $ sanitize xs] go (Note xs) = Note $ bottomUp go' xs @@ -124,9 +109,11 @@ getCitation :: Inline -> [[Citation]] getCitation i | Cite t _ <- i = [t] | otherwise = [] -setHash :: Citation -> IO Citation -setHash (Citation i p s cm nn _) - = hashUnique `fmap` newUnique >>= return . Citation i p s cm nn +setHash :: Citation -> State Int Citation +setHash c = do + ident <- get + put $ ident + 1 + return c{ citationHash = ident } toCslCite :: Citation -> CSL.Cite toCslCite c @@ -165,7 +152,7 @@ locatorWords inp = breakup (x : xs) = x : breakup xs splitup = groupBy (\x y -> x /= '\160' && y /= '\160') -pLocatorWords :: GenParser Inline st (String, [Inline]) +pLocatorWords :: Parsec [Inline] st (String, [Inline]) pLocatorWords = do l <- pLocator s <- getInput -- rest is suffix @@ -173,16 +160,16 @@ pLocatorWords = do then return (init l, Str "," : s) else return (l, s) -pMatch :: (Inline -> Bool) -> GenParser Inline st Inline +pMatch :: (Inline -> Bool) -> Parsec [Inline] st Inline pMatch condition = try $ do t <- anyToken guard $ condition t return t -pSpace :: GenParser Inline st Inline +pSpace :: Parsec [Inline] st Inline pSpace = pMatch (\t -> t == Space || t == Str "\160") -pLocator :: GenParser Inline st String +pLocator :: Parsec [Inline] st String pLocator = try $ do optional $ pMatch (== Str ",") optional pSpace @@ -190,7 +177,7 @@ pLocator = try $ do gs <- many1 pWordWithDigits return $ stringify f ++ (' ' : unwords gs) -pWordWithDigits :: GenParser Inline st String +pWordWithDigits :: Parsec [Inline] st String pWordWithDigits = try $ do pSpace r <- many1 (notFollowedBy pSpace >> anyToken) diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 080acebee..95df88099 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -47,6 +47,7 @@ module Text.Pandoc.Highlighting ( languages , Style ) where import Text.Pandoc.Definition +import Text.Pandoc.Shared (safeRead) import Text.Highlighting.Kate import Data.List (find) import Data.Maybe (fromMaybe) @@ -60,9 +61,9 @@ highlight :: (FormatOptions -> [SourceLine] -> a) -- ^ Formatter -> String -- ^ Raw contents of the CodeBlock -> Maybe a -- ^ Maybe the formatted result highlight formatter (_, classes, keyvals) rawCode = - let firstNum = case reads (fromMaybe "1" $ lookup "startFrom" keyvals) of - ((n,_):_) -> n - [] -> 1 + let firstNum = case safeRead (fromMaybe "1" $ lookup "startFrom" keyvals) of + Just n -> n + Nothing -> 1 fmtOpts = defaultFormatOpts{ startNumber = firstNum, numberLines = any (`elem` diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index d48c6a5ae..f97cda28c 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -29,9 +29,9 @@ Portability : portable Functions for determining the size of a PNG, JPEG, or GIF image. -} module Text.Pandoc.ImageSize ( ImageType(..), imageType, imageSize, - sizeInPixels, sizeInPoints, readImageSize ) where -import Data.ByteString.Lazy (ByteString, unpack) -import qualified Data.ByteString.Lazy.Char8 as B + sizeInPixels, sizeInPoints ) where +import Data.ByteString (ByteString, unpack) +import qualified Data.ByteString.Char8 as B import Control.Monad import Data.Bits @@ -48,9 +48,6 @@ data ImageSize = ImageSize{ } deriving (Read, Show, Eq) -readImageSize :: FilePath -> IO (Maybe ImageSize) -readImageSize fp = imageSize `fmap` B.readFile fp - imageType :: ByteString -> Maybe ImageType imageType img = case B.take 4 img of "\x89\x50\x4e\x47" -> return Png diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index f9749cece..eb54bd48d 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MIME Copyright : Copyright (C) 2011 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -141,6 +141,7 @@ getMimeType f = M.lookup (map toLower $ drop 1 $ takeExtension f) mimeTypes ,("embl","chemical/x-embl-dl-nucleotide") ,("eml","message/rfc822") ,("ent","chemical/x-ncbi-asn1-ascii") + ,("eot","application/vnd.ms-fontobject") ,("eps","application/postscript") ,("etx","text/x-setext") ,("exe","application/x-msdos-program") @@ -155,6 +156,7 @@ getMimeType f = M.lookup (map toLower $ drop 1 $ takeExtension f) mimeTypes ,("fm","application/x-maker") ,("frame","application/x-maker") ,("frm","application/x-maker") + ,("fs","text/plain") ,("gal","chemical/x-gaussian-log") ,("gam","chemical/x-gamess-input") ,("gamin","chemical/x-gamess-input") @@ -442,6 +444,7 @@ getMimeType f = M.lookup (map toLower $ drop 1 $ takeExtension f) mimeTypes ,("vms","chemical/x-vamas-iso14976") ,("vrm","x-world/x-vrml") ,("vrml","model/vrml") + ,("vs","text/plain") ,("vsd","application/vnd.visio") ,("wad","application/x-doom") ,("wav","audio/x-wav") @@ -460,6 +463,7 @@ getMimeType f = M.lookup (map toLower $ drop 1 $ takeExtension f) mimeTypes ,("wmv","video/x-ms-wmv") ,("wmx","video/x-ms-wmx") ,("wmz","application/x-ms-wmz") + ,("woff","application/x-font-woff") ,("wp5","application/wordperfect5.1") ,("wpd","application/wordperfect") ,("wrl","model/vrml") diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs new file mode 100644 index 000000000..f67debf97 --- /dev/null +++ b/src/Text/Pandoc/Options.hs @@ -0,0 +1,347 @@ +{- +Copyright (C) 2012 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.Options + Copyright : Copyright (C) 2012 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Data structures and functions for representing parser and writer +options. +-} +module Text.Pandoc.Options ( Extension(..) + , pandocExtensions + , strictExtensions + , phpMarkdownExtraExtensions + , githubMarkdownExtensions + , multimarkdownExtensions + , ReaderOptions(..) + , HTMLMathMethod (..) + , CiteMethod (..) + , ObfuscationMethod (..) + , HTMLSlideVariant (..) + , EPUBVersion (..) + , WriterOptions (..) + , def + , isEnabled + ) where +import Data.Set (Set) +import qualified Data.Set as Set +import Data.Default +import Text.Pandoc.Highlighting (Style, pygments) +import qualified Text.CSL as CSL + +-- | Individually selectable syntax extensions. +data Extension = + Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes + | Ext_inline_notes -- ^ Pandoc-style inline notes + | Ext_pandoc_title_block -- ^ Pandoc title block + | Ext_mmd_title_block -- ^ Multimarkdown metadata block + | Ext_table_captions -- ^ Pandoc-style table captions + | Ext_implicit_figures -- ^ A paragraph with just an image is a figure + | Ext_simple_tables -- ^ Pandoc-style simple tables + | Ext_multiline_tables -- ^ Pandoc-style multiline tables + | Ext_grid_tables -- ^ Grid tables (pandoc, reST) + | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) + | Ext_citations -- ^ Pandoc/citeproc citations + | Ext_raw_tex -- ^ Allow raw TeX (other than math) + | Ext_raw_html -- ^ Allow raw HTML + | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ + | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] + | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] + | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only) + | Ext_fenced_code_blocks -- ^ Parse fenced code blocks + | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks + | Ext_backtick_code_blocks -- ^ Github style ``` code blocks + | Ext_inline_code_attributes -- ^ Allow attributes on inline code + | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks + | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown + -- iff container has attribute 'markdown' + | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak + | Ext_link_attributes -- ^ MMD style reference link attributes + | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links + | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters + | Ext_startnum -- ^ Make start number of ordered list significant + | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php + | Ext_example_lists -- ^ Markdown-style numbered examples + | Ext_all_symbols_escapable -- ^ Make all non-alphanumerics escapable + | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote + | Ext_blank_before_header -- ^ Require blank line before a header + | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax + | Ext_superscript -- ^ Superscript using ^this^ syntax + | Ext_subscript -- ^ Subscript using ~this~ syntax + | Ext_hard_line_breaks -- ^ All newlines become hard line breaks + | Ext_literate_haskell -- ^ Enable literate Haskell conventions + | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions + | Ext_auto_identifiers -- ^ Automatic identifiers for headers + | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} + | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_implicit_header_references -- ^ Implicit reference links for headers + | Ext_line_blocks -- ^ RST style line blocks + deriving (Show, Read, Enum, Eq, Ord, Bounded) + +pandocExtensions :: Set Extension +pandocExtensions = Set.fromList + [ Ext_footnotes + , Ext_inline_notes + , Ext_pandoc_title_block + , Ext_table_captions + , Ext_implicit_figures + , Ext_simple_tables + , Ext_multiline_tables + , Ext_grid_tables + , Ext_pipe_tables + , Ext_citations + , Ext_raw_tex + , Ext_raw_html + , Ext_tex_math_dollars + , Ext_latex_macros + , Ext_fenced_code_blocks + , Ext_fenced_code_attributes + , Ext_backtick_code_blocks + , Ext_inline_code_attributes + , Ext_markdown_in_html_blocks + , Ext_escaped_line_breaks + , Ext_fancy_lists + , Ext_startnum + , Ext_definition_lists + , Ext_example_lists + , Ext_all_symbols_escapable + , Ext_intraword_underscores + , Ext_blank_before_blockquote + , Ext_blank_before_header + , Ext_strikeout + , Ext_superscript + , Ext_subscript + , Ext_auto_identifiers + , Ext_header_attributes + , Ext_implicit_header_references + , Ext_line_blocks + ] + +phpMarkdownExtraExtensions :: Set Extension +phpMarkdownExtraExtensions = Set.fromList + [ Ext_footnotes + , Ext_pipe_tables + , Ext_raw_html + , Ext_markdown_attribute + , Ext_fenced_code_blocks + , Ext_definition_lists + , Ext_intraword_underscores + , Ext_header_attributes + , Ext_abbreviations + ] + +githubMarkdownExtensions :: Set Extension +githubMarkdownExtensions = Set.fromList + [ Ext_pipe_tables + , Ext_raw_html + , Ext_tex_math_single_backslash + , Ext_fenced_code_blocks + , Ext_fenced_code_attributes + , Ext_backtick_code_blocks + , Ext_autolink_bare_uris + , Ext_intraword_underscores + , Ext_strikeout + , Ext_hard_line_breaks + ] + +multimarkdownExtensions :: Set Extension +multimarkdownExtensions = Set.fromList + [ Ext_pipe_tables + , Ext_raw_html + , Ext_markdown_attribute + , Ext_link_attributes + , Ext_raw_tex + , Ext_tex_math_double_backslash + , Ext_intraword_underscores + , Ext_mmd_title_block + , Ext_footnotes + , Ext_definition_lists + , Ext_all_symbols_escapable + , Ext_implicit_header_references + , Ext_auto_identifiers + , Ext_mmd_header_identifiers + ] + +strictExtensions :: Set Extension +strictExtensions = Set.fromList + [ Ext_raw_html ] + +data ReaderOptions = ReaderOptions{ + readerExtensions :: Set Extension -- ^ Syntax extensions + , readerSmart :: Bool -- ^ Smart punctuation + , readerStrict :: Bool -- ^ FOR TRANSITION ONLY + , readerStandalone :: Bool -- ^ Standalone document with header + , readerParseRaw :: Bool -- ^ Parse raw HTML, LaTeX + , readerColumns :: Int -- ^ Number of columns in terminal + , readerTabStop :: Int -- ^ Tab stop + , readerOldDashes :: Bool -- ^ Use pandoc <= 1.8.2.1 behavior + -- in parsing dashes; -- is em-dash; + -- - before numerial is en-dash + , readerReferences :: [CSL.Reference] -- ^ Bibliographic references + , readerCitationStyle :: Maybe CSL.Style -- ^ Citation style + , readerApplyMacros :: Bool -- ^ Apply macros to TeX math + , readerIndentedCodeClasses :: [String] -- ^ Default classes for + -- indented code blocks +} deriving (Show, Read) + +instance Default ReaderOptions + where def = ReaderOptions{ + readerExtensions = pandocExtensions + , readerSmart = False + , readerStrict = False + , readerStandalone = False + , readerParseRaw = False + , readerColumns = 80 + , readerTabStop = 4 + , readerOldDashes = False + , readerReferences = [] + , readerCitationStyle = Nothing + , readerApplyMacros = True + , readerIndentedCodeClasses = [] + } + +-- +-- Writer options +-- + +data EPUBVersion = EPUB2 | EPUB3 deriving (Eq, Show, Read) + +data HTMLMathMethod = PlainMath + | LaTeXMathML (Maybe String) -- url of LaTeXMathML.js + | JsMath (Maybe String) -- url of jsMath load script + | GladTeX + | WebTeX String -- url of TeX->image script. + | MathML (Maybe String) -- url of MathMLinHTML.js + | MathJax String -- url of MathJax.js + deriving (Show, Read, Eq) + +data CiteMethod = Citeproc -- use citeproc to render them + | Natbib -- output natbib cite commands + | Biblatex -- output biblatex cite commands + deriving (Show, Read, Eq) + +-- | Methods for obfuscating email addresses in HTML. +data ObfuscationMethod = NoObfuscation + | ReferenceObfuscation + | JavascriptObfuscation + deriving (Show, Read, Eq) + +-- | Varieties of HTML slide shows. +data HTMLSlideVariant = S5Slides + | SlidySlides + | SlideousSlides + | DZSlides + | NoSlides + deriving (Show, Read, Eq) + +-- | Options for writers +data WriterOptions = WriterOptions + { writerStandalone :: Bool -- ^ Include header and footer + , writerTemplate :: String -- ^ Template to use in standalone mode + , writerVariables :: [(String, String)] -- ^ Variables to set in template + , writerTabStop :: Int -- ^ Tabstop for conversion btw spaces and tabs + , writerTableOfContents :: Bool -- ^ Include table of contents + , writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous? + , writerIncremental :: Bool -- ^ True if lists should be incremental + , writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML + , writerIgnoreNotes :: Bool -- ^ Ignore footnotes (used in making toc) + , writerNumberSections :: Bool -- ^ Number sections in LaTeX + , writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML + , writerExtensions :: Set Extension -- ^ Markdown extensions that can be used + , writerReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst + , writerWrapText :: Bool -- ^ Wrap text to line length + , writerColumns :: Int -- ^ Characters in a line (for text wrapping) + , writerEmailObfuscation :: ObfuscationMethod -- ^ How to obfuscate emails + , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML + -- and for footnote marks in markdown + , writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file + , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory + , writerCiteMethod :: CiteMethod -- ^ How to print cites + , writerBiblioFiles :: [FilePath] -- ^ Biblio files to use for citations + , writerHtml5 :: Bool -- ^ Produce HTML5 + , writerHtmlQTags :: Bool -- ^ Use @<q>@ tags for quotes in HTML + , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show + , writerSlideLevel :: Maybe Int -- ^ Force header level of slides + , writerChapters :: Bool -- ^ Use "chapter" for top-level sects + , writerListings :: Bool -- ^ Use listings package for code + , writerHighlight :: Bool -- ^ Highlight source code + , writerHighlightStyle :: Style -- ^ Style to use for highlighting + , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown + , writerTeXLigatures :: Bool -- ^ Use tex ligatures quotes, dashes in latex + , writerEpubVersion :: Maybe EPUBVersion -- ^ Nothing or EPUB version + , writerEpubMetadata :: String -- ^ Metadata to include in EPUB + , writerEpubStylesheet :: Maybe String -- ^ EPUB stylesheet specified at command line + , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed + , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) + , writerTOCDepth :: Int -- ^ Number of levels to include in TOC + , writerReferenceODT :: Maybe FilePath -- ^ Path to reference ODT if specified + , writerReferenceDocx :: Maybe FilePath -- ^ Ptah to reference DOCX if specified + } deriving Show + +instance Default WriterOptions where + def = WriterOptions { writerStandalone = False + , writerTemplate = "" + , writerVariables = [] + , writerTabStop = 4 + , writerTableOfContents = False + , writerSlideVariant = NoSlides + , writerIncremental = False + , writerHTMLMathMethod = PlainMath + , writerIgnoreNotes = False + , writerNumberSections = False + , writerSectionDivs = False + , writerExtensions = pandocExtensions + , writerReferenceLinks = False + , writerWrapText = True + , writerColumns = 72 + , writerEmailObfuscation = JavascriptObfuscation + , writerIdentifierPrefix = "" + , writerSourceDirectory = "." + , writerUserDataDir = Nothing + , writerCiteMethod = Citeproc + , writerBiblioFiles = [] + , writerHtml5 = False + , writerHtmlQTags = False + , writerBeamer = False + , writerSlideLevel = Nothing + , writerChapters = False + , writerListings = False + , writerHighlight = False + , writerHighlightStyle = pygments + , writerSetextHeaders = True + , writerTeXLigatures = True + , writerEpubVersion = Nothing + , writerEpubMetadata = "" + , writerEpubStylesheet = Nothing + , writerEpubFonts = [] + , writerEpubChapterLevel = 1 + , writerTOCDepth = 3 + , writerReferenceODT = Nothing + , writerReferenceDocx = Nothing + } + +-- | Returns True if the given extension is enabled. +isEnabled :: Extension -> WriterOptions -> Bool +isEnabled ext opts = ext `Set.member` (writerExtensions opts) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 140b96cfa..26e8c2325 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {- Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu> @@ -19,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Parsing Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -32,6 +33,7 @@ module Text.Pandoc.Parsing ( (>>~), many1Till, notFollowedBy', oneOfStrings, + oneOfStringsCI, spaceChar, nonspaceChar, skipSpaces, @@ -47,47 +49,139 @@ module Text.Pandoc.Parsing ( (>>~), uri, withHorizDisplacement, withRaw, - nullBlock, - failIfStrict, - failUnlessLHS, escaped, characterReference, updateLastStrPos, anyOrderedListMarker, orderedListMarker, charRef, + lineBlockLines, tableWith, + widthsFromIndices, gridTableWith, readWith, testStringWith, + getOption, + guardEnabled, + guardDisabled, ParserState (..), defaultParserState, HeaderType (..), ParserContext (..), QuoteContext (..), NoteTable, + NoteTable', KeyTable, - Key, + SubstTable, + Key (..), toKey, - fromKey, - lookupKeySrc, smartPunctuation, + withQuoteContext, + singleQuoteStart, + singleQuoteEnd, + doubleQuoteStart, + doubleQuoteEnd, + ellipses, + apostrophe, + dash, + nested, macro, - applyMacros' ) + applyMacros', + Parser, + F(..), + runF, + askF, + asksF, + -- * Re-exports from Text.Pandoc.Parsec + runParser, + parse, + anyToken, + getInput, + setInput, + unexpected, + char, + letter, + digit, + alphaNum, + skipMany, + skipMany1, + spaces, + space, + anyChar, + satisfy, + newline, + string, + count, + eof, + noneOf, + oneOf, + lookAhead, + notFollowedBy, + many, + many1, + manyTill, + (<|>), + (<?>), + choice, + try, + sepBy, + sepBy1, + sepEndBy, + sepEndBy1, + endBy, + endBy1, + option, + optional, + optionMaybe, + getState, + setState, + updateState, + SourcePos, + getPosition, + setPosition, + sourceColumn, + sourceLine, + newPos, + token + ) where import Text.Pandoc.Definition -import Text.Pandoc.Generic +import Text.Pandoc.Options +import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) -import Text.ParserCombinators.Parsec -import Data.Char ( toLower, toUpper, ord, isAscii, isAlphaNum, isDigit, isPunctuation ) +import Text.Parsec +import Text.Parsec.Pos (newPos) +import Data.Char ( toLower, toUpper, ord, isAscii, isAlphaNum, isDigit, isHexDigit, + isSpace ) import Data.List ( intercalate, transpose ) -import Network.URI ( parseURI, URI (..), isAllowedInURI ) -import Control.Monad ( join, liftM, guard ) import Text.Pandoc.Shared import qualified Data.Map as M import Text.TeXMath.Macros (applyMacros, Macro, parseMacroDefinitions) import Text.HTML.TagSoup.Entity ( lookupEntity ) +import Data.Default +import qualified Data.Set as Set +import Control.Monad.Reader +import Control.Applicative ((*>), (<*), (<$), liftA2) +import Data.Monoid + +type Parser t s = Parsec t s + +newtype F a = F { unF :: Reader ParserState a } deriving (Monad, Functor) + +runF :: F a -> ParserState -> a +runF = runReader . unF + +askF :: F ParserState +askF = F ask + +asksF :: (ParserState -> a) -> F a +asksF f = F $ asks f + +instance Monoid a => Monoid (F a) where + mempty = return mempty + mappend = liftM2 mappend + mconcat = liftM mconcat . sequence -- | Like >>, but returns the operation on the left. -- (Suggested by Tillmann Rendel on Haskell-cafe list.) @@ -95,62 +189,81 @@ import Text.HTML.TagSoup.Entity ( lookupEntity ) a >>~ b = a >>= \x -> b >> return x -- | Parse any line of text -anyLine :: GenParser Char st [Char] +anyLine :: Parser [Char] st [Char] anyLine = manyTill anyChar newline -- | Like @manyTill@, but reads at least one item. -many1Till :: GenParser tok st a - -> GenParser tok st end - -> GenParser tok st [a] +many1Till :: Parser [tok] st a + -> Parser [tok] st end + -> Parser [tok] st [a] many1Till p end = do first <- p rest <- manyTill p end return (first:rest) --- | A more general form of @notFollowedBy@. This one allows any +-- | A more general form of @notFollowedBy@. This one allows any -- type of parser to be specified, and succeeds only if that parser fails. -- It does not consume any input. -notFollowedBy' :: Show b => GenParser a st b -> GenParser a st () +notFollowedBy' :: Show b => Parser [a] st b -> Parser [a] st () notFollowedBy' p = try $ join $ do a <- try p return (unexpected (show a)) <|> return (return ()) -- (This version due to Andrew Pimlott on the Haskell mailing list.) --- | Parses one of a list of strings (tried in order). -oneOfStrings :: [String] -> GenParser Char st String -oneOfStrings listOfStrings = choice $ map (try . string) listOfStrings +oneOfStrings' :: (Char -> Char -> Bool) -> [String] -> Parser [Char] st String +oneOfStrings' _ [] = fail "no strings" +oneOfStrings' matches strs = try $ do + c <- anyChar + let strs' = [xs | (x:xs) <- strs, x `matches` c] + case strs' of + [] -> fail "not found" + _ -> (c:) `fmap` oneOfStrings' matches strs' + <|> if "" `elem` strs' + then return [c] + else fail "not found" + +-- | Parses one of a list of strings. If the list contains +-- two strings one of which is a prefix of the other, the longer +-- string will be matched if possible. +oneOfStrings :: [String] -> Parser [Char] st String +oneOfStrings = oneOfStrings' (==) + +-- | Parses one of a list of strings (tried in order), case insensitive. +oneOfStringsCI :: [String] -> Parser [Char] st String +oneOfStringsCI = oneOfStrings' ciMatch + where ciMatch x y = toLower x == toLower y -- | Parses a space or tab. -spaceChar :: CharParser st Char +spaceChar :: Parser [Char] st Char spaceChar = satisfy $ \c -> c == ' ' || c == '\t' -- | Parses a nonspace, nonnewline character. -nonspaceChar :: CharParser st Char +nonspaceChar :: Parser [Char] st Char nonspaceChar = satisfy $ \x -> x /= '\t' && x /= '\n' && x /= ' ' && x /= '\r' -- | Skips zero or more spaces or tabs. -skipSpaces :: GenParser Char st () +skipSpaces :: Parser [Char] st () skipSpaces = skipMany spaceChar -- | Skips zero or more spaces or tabs, then reads a newline. -blankline :: GenParser Char st Char +blankline :: Parser [Char] st Char blankline = try $ skipSpaces >> newline -- | Parses one or more blank lines and returns a string of newlines. -blanklines :: GenParser Char st [Char] +blanklines :: Parser [Char] st [Char] blanklines = many1 blankline -- | Parses material enclosed between start and end parsers. -enclosed :: GenParser Char st t -- ^ start parser - -> GenParser Char st end -- ^ end parser - -> GenParser Char st a -- ^ content parser (to be used repeatedly) - -> GenParser Char st [a] -enclosed start end parser = try $ +enclosed :: Parser [Char] st t -- ^ start parser + -> Parser [Char] st end -- ^ end parser + -> Parser [Char] st a -- ^ content parser (to be used repeatedly) + -> Parser [Char] st [a] +enclosed start end parser = try $ start >> notFollowedBy space >> many1Till parser end -- | Parse string, case insensitive. -stringAnyCase :: [Char] -> CharParser st String +stringAnyCase :: [Char] -> Parser [Char] st String stringAnyCase [] = string "" stringAnyCase (x:xs) = do firstChar <- char (toUpper x) <|> char (toLower x) @@ -158,7 +271,7 @@ stringAnyCase (x:xs) = do return (firstChar:rest) -- | Parse contents of 'str' using 'parser' and return result. -parseFromString :: GenParser tok st a -> [tok] -> GenParser tok st a +parseFromString :: Parser [tok] st a -> [tok] -> Parser [tok] st a parseFromString parser str = do oldPos <- getPosition oldInput <- getInput @@ -169,8 +282,8 @@ parseFromString parser str = do return result -- | Parse raw line block up to and including blank lines. -lineClump :: GenParser Char st String -lineClump = blanklines +lineClump :: Parser [Char] st String +lineClump = blanklines <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines) -- | Parse a string of characters between an open character @@ -178,8 +291,8 @@ lineClump = blanklines -- pairs of open and close, which must be different. For example, -- @charsInBalanced '(' ')' anyChar@ will parse "(hello (there))" -- and return "hello (there)". -charsInBalanced :: Char -> Char -> GenParser Char st Char - -> GenParser Char st String +charsInBalanced :: Char -> Char -> Parser [Char] st Char + -> Parser [Char] st String charsInBalanced open close parser = try $ do char open let isDelim c = c == open || c == close @@ -204,13 +317,13 @@ uppercaseRomanDigits = map toUpper lowercaseRomanDigits -- | Parses a roman numeral (uppercase or lowercase), returns number. romanNumeral :: Bool -- ^ Uppercase if true - -> GenParser Char st Int + -> Parser [Char] st Int romanNumeral upperCase = do - let romanDigits = if upperCase - then uppercaseRomanDigits + let romanDigits = if upperCase + then uppercaseRomanDigits else lowercaseRomanDigits lookAhead $ oneOf romanDigits - let [one, five, ten, fifty, hundred, fivehundred, thousand] = + let [one, five, ten, fifty, hundred, fivehundred, thousand] = map char romanDigits thousands <- many thousand >>= (return . (1000 *) . length) ninehundreds <- option 0 $ try $ hundred >> thousand >> return 900 @@ -234,68 +347,90 @@ romanNumeral upperCase = do -- Parsers for email addresses and URIs -emailChar :: GenParser Char st Char -emailChar = alphaNum <|> - satisfy (\c -> c == '-' || c == '+' || c == '_' || c == '.') - -domainChar :: GenParser Char st Char -domainChar = alphaNum <|> char '-' - -domain :: GenParser Char st [Char] -domain = do - first <- many1 domainChar - dom <- many1 $ try (char '.' >> many1 domainChar ) - return $ intercalate "." (first:dom) - -- | Parses an email address; returns original and corresponding -- escaped mailto: URI. -emailAddress :: GenParser Char st (String, String) -emailAddress = try $ do - firstLetter <- alphaNum - restAddr <- many emailChar - let addr = firstLetter:restAddr - char '@' - dom <- domain - let full = addr ++ '@':dom - return (full, escapeURI $ "mailto:" ++ full) +emailAddress :: Parser [Char] st (String, String) +emailAddress = try $ liftA2 toResult mailbox (char '@' *> domain) + where toResult mbox dom = let full = mbox ++ '@':dom + in (full, escapeURI $ "mailto:" ++ full) + mailbox = intercalate "." `fmap` (emailWord `sepby1` dot) + domain = intercalate "." `fmap` (subdomain `sepby1` dot) + dot = char '.' + subdomain = many1 $ alphaNum <|> innerPunct + innerPunct = try (satisfy (\c -> isEmailPunct c || c == '@') <* + notFollowedBy space) + emailWord = many1 $ satisfy isEmailChar + isEmailChar c = isAlphaNum c || isEmailPunct c + isEmailPunct c = c `elem` "!\"#$%&'*+-/=?^_{|}~" + -- note: sepBy1 from parsec consumes input when sep + -- succeeds and p fails, so we use this variant here. + sepby1 p sep = liftA2 (:) p (many (try $ sep >> p)) + + +-- Schemes from http://www.iana.org/assignments/uri-schemes.html plus +-- the unofficial schemes coap, doi, javascript. +schemes :: [String] +schemes = ["coap","doi","javascript","aaa","aaas","about","acap","cap","cid", + "crid","data","dav","dict","dns","file","ftp","geo","go","gopher", + "h323","http","https","iax","icap","im","imap","info","ipp","iris", + "iris.beep","iris.xpc","iris.xpcs","iris.lwz","ldap","mailto","mid", + "msrp","msrps","mtqp","mupdate","news","nfs","ni","nih","nntp", + "opaquelocktoken","pop","pres","rtsp","service","session","shttp","sieve", + "sip","sips","sms","snmp","soap.beep","soap.beeps","tag","tel","telnet", + "tftp","thismessage","tn3270","tip","tv","urn","vemmi","ws","wss","xcon", + "xcon-userid","xmlrpc.beep","xmlrpc.beeps","xmpp","z39.50r","z39.50s", + "adiumxtra","afp","afs","aim","apt","attachment","aw","beshare","bitcoin", + "bolo","callto","chrome","chrome-extension","com-eventbrite-attendee", + "content", "cvs","dlna-playsingle","dlna-playcontainer","dtn","dvb", + "ed2k","facetime","feed","finger","fish","gg","git","gizmoproject", + "gtalk","hcp","icon","ipn","irc","irc6","ircs","itms","jar","jms", + "keyparc","lastfm","ldaps","magnet","maps","market","message","mms", + "ms-help","msnim","mumble","mvn","notes","oid","palm","paparazzi", + "platform","proxy","psyc","query","res","resource","rmi","rsync", + "rtmp","secondlife","sftp","sgn","skype","smb","soldat","spotify", + "ssh","steam","svn","teamspeak","things","udp","unreal","ut2004", + "ventrilo","view-source","webcal","wtai","wyciwyg","xfire","xri", + "ymsgr"] + +uriScheme :: Parser [Char] st String +uriScheme = oneOfStringsCI schemes -- | Parses a URI. Returns pair of original and URI-escaped version. -uri :: GenParser Char st (String, String) +uri :: Parser [Char] st (String, String) uri = try $ do - let protocols = [ "http:", "https:", "ftp:", "file:", "mailto:", - "news:", "telnet:" ] - lookAhead $ oneOfStrings protocols - -- Scan non-ascii characters and ascii characters allowed in a URI. - -- We allow punctuation except when followed by a space, since - -- we don't want the trailing '.' in 'http://google.com.' - let innerPunct = try $ satisfy isPunctuation >>~ - notFollowedBy (newline <|> spaceChar) - let uriChar = innerPunct <|> - satisfy (\c -> not (isPunctuation c) && - (not (isAscii c) || isAllowedInURI c)) - -- We want to allow + scheme <- uriScheme + char ':' + -- /^[\/\w\u0080-\uffff]+|%[A-Fa-f0-9]+|&#?\w+;|(?:[,]+|[\S])[%&~\w\u0080-\uffff]/ + -- We allow punctuation except at the end, since + -- we don't want the trailing '.' in 'http://google.com.' We want to allow -- http://en.wikipedia.org/wiki/State_of_emergency_(disambiguation) -- as a URL, while NOT picking up the closing paren in - -- (http://wikipedia.org) - -- So we include balanced parens in the URL. - let inParens = try $ do char '(' - res <- many uriChar - char ')' - return $ '(' : res ++ ")" - str <- liftM concat $ many1 $ inParens <|> count 1 (innerPunct <|> uriChar) - -- now see if they amount to an absolute URI - case parseURI (escapeURI str) of - Just uri' -> if uriScheme uri' `elem` protocols - then return (str, show uri') - else fail "not a URI" - Nothing -> fail "not a URI" + -- (http://wikipedia.org). So we include balanced parens in the URL. + let isWordChar c = isAlphaNum c || c == '_' || c == '/' || not (isAscii c) + let wordChar = satisfy isWordChar + let percentEscaped = try $ char '%' >> skipMany1 (satisfy isHexDigit) + let entity = () <$ characterReference + let punct = skipMany1 (char ',') + <|> () <$ (satisfy (not . isSpace)) + let uriChunk = skipMany1 wordChar + <|> percentEscaped + <|> entity + <|> (try $ punct >> notFollowedBy (satisfy $ not . isWordChar)) + str <- snd `fmap` withRaw (skipMany1 ( () <$ + (enclosed (char '(') (char ')') uriChunk + <|> enclosed (char '{') (char '}') uriChunk + <|> enclosed (char '[') (char ']') uriChunk) + <|> uriChunk)) + str' <- option str $ char '/' >> return (str ++ "/") + let uri' = scheme ++ ":" ++ str' + return (uri', escapeURI uri') -- | Applies a parser, returns tuple of its results and its horizontal -- displacement (the difference between the source column at the end -- and the source column at the beginning). Vertical displacement -- (source row) is ignored. -withHorizDisplacement :: GenParser Char st a -- ^ Parser to apply - -> GenParser Char st (a, Int) -- ^ (result, displacement) +withHorizDisplacement :: Parser [Char] st a -- ^ Parser to apply + -> Parser [Char] st (a, Int) -- ^ (result, displacement) withHorizDisplacement parser = do pos1 <- getPosition result <- parser @@ -304,7 +439,7 @@ withHorizDisplacement parser = do -- | Applies a parser and returns the raw string that was parsed, -- along with the value produced by the parser. -withRaw :: GenParser Char st a -> GenParser Char st (a, [Char]) +withRaw :: Parser [Char] st a -> Parser [Char] st (a, [Char]) withRaw parser = do pos1 <- getPosition inp <- getInput @@ -314,33 +449,18 @@ withRaw parser = do let (l2,c2) = (sourceLine pos2, sourceColumn pos2) let inplines = take ((l2 - l1) + 1) $ lines inp let raw = case inplines of - [] -> error "raw: inplines is null" -- shouldn't happen + [] -> "" [l] -> take (c2 - c1) l ls -> unlines (init ls) ++ take (c2 - 1) (last ls) return (result, raw) --- | Parses a character and returns 'Null' (so that the parser can move on --- if it gets stuck). -nullBlock :: GenParser Char st Block -nullBlock = anyChar >> return Null - --- | Fail if reader is in strict markdown syntax mode. -failIfStrict :: GenParser a ParserState () -failIfStrict = do - state <- getState - if stateStrict state then fail "strict mode" else return () - --- | Fail unless we're in literate haskell mode. -failUnlessLHS :: GenParser tok ParserState () -failUnlessLHS = getState >>= guard . stateLiterateHaskell - -- | Parses backslash, then applies character parser. -escaped :: GenParser Char st Char -- ^ Parser for character to escape - -> GenParser Char st Char +escaped :: Parser [Char] st Char -- ^ Parser for character to escape + -> Parser [Char] st Char escaped parser = try $ char '\\' >> parser -- | Parse character entity. -characterReference :: GenParser Char st Char +characterReference :: Parser [Char] st Char characterReference = try $ do char '&' ent <- many1Till nonspaceChar (char ';') @@ -349,19 +469,19 @@ characterReference = try $ do Nothing -> fail "entity not found" -- | Parses an uppercase roman numeral and returns (UpperRoman, number). -upperRoman :: GenParser Char st (ListNumberStyle, Int) +upperRoman :: Parser [Char] st (ListNumberStyle, Int) upperRoman = do num <- romanNumeral True return (UpperRoman, num) -- | Parses a lowercase roman numeral and returns (LowerRoman, number). -lowerRoman :: GenParser Char st (ListNumberStyle, Int) +lowerRoman :: Parser [Char] st (ListNumberStyle, Int) lowerRoman = do num <- romanNumeral False return (LowerRoman, num) -- | Parses a decimal numeral and returns (Decimal, number). -decimal :: GenParser Char st (ListNumberStyle, Int) +decimal :: Parser [Char] st (ListNumberStyle, Int) decimal = do num <- many1 digit return (Decimal, read num) @@ -370,7 +490,7 @@ decimal = do -- returns (DefaultStyle, [next example number]). The next -- example number is incremented in parser state, and the label -- (if present) is added to the label table. -exampleNum :: GenParser Char ParserState (ListNumberStyle, Int) +exampleNum :: Parser [Char] ParserState (ListNumberStyle, Int) exampleNum = do char '@' lab <- many (alphaNum <|> satisfy (\c -> c == '_' || c == '-')) @@ -384,38 +504,38 @@ exampleNum = do return (Example, num) -- | Parses a '#' returns (DefaultStyle, 1). -defaultNum :: GenParser Char st (ListNumberStyle, Int) +defaultNum :: Parser [Char] st (ListNumberStyle, Int) defaultNum = do char '#' return (DefaultStyle, 1) -- | Parses a lowercase letter and returns (LowerAlpha, number). -lowerAlpha :: GenParser Char st (ListNumberStyle, Int) +lowerAlpha :: Parser [Char] st (ListNumberStyle, Int) lowerAlpha = do ch <- oneOf ['a'..'z'] return (LowerAlpha, ord ch - ord 'a' + 1) -- | Parses an uppercase letter and returns (UpperAlpha, number). -upperAlpha :: GenParser Char st (ListNumberStyle, Int) +upperAlpha :: Parser [Char] st (ListNumberStyle, Int) upperAlpha = do ch <- oneOf ['A'..'Z'] return (UpperAlpha, ord ch - ord 'A' + 1) -- | Parses a roman numeral i or I -romanOne :: GenParser Char st (ListNumberStyle, Int) +romanOne :: Parser [Char] st (ListNumberStyle, Int) romanOne = (char 'i' >> return (LowerRoman, 1)) <|> (char 'I' >> return (UpperRoman, 1)) -- | Parses an ordered list marker and returns list attributes. -anyOrderedListMarker :: GenParser Char ParserState ListAttributes -anyOrderedListMarker = choice $ +anyOrderedListMarker :: Parser [Char] ParserState ListAttributes +anyOrderedListMarker = choice $ [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens], numParser <- [decimal, exampleNum, defaultNum, romanOne, lowerAlpha, lowerRoman, upperAlpha, upperRoman]] -- | Parses a list number (num) followed by a period, returns list attributes. -inPeriod :: GenParser Char st (ListNumberStyle, Int) - -> GenParser Char st ListAttributes +inPeriod :: Parser [Char] st (ListNumberStyle, Int) + -> Parser [Char] st ListAttributes inPeriod num = try $ do (style, start) <- num char '.' @@ -423,18 +543,18 @@ inPeriod num = try $ do then DefaultDelim else Period return (start, style, delim) - + -- | Parses a list number (num) followed by a paren, returns list attributes. -inOneParen :: GenParser Char st (ListNumberStyle, Int) - -> GenParser Char st ListAttributes +inOneParen :: Parser [Char] st (ListNumberStyle, Int) + -> Parser [Char] st ListAttributes inOneParen num = try $ do (style, start) <- num char ')' return (start, style, OneParen) -- | Parses a list number (num) enclosed in parens, returns list attributes. -inTwoParens :: GenParser Char st (ListNumberStyle, Int) - -> GenParser Char st ListAttributes +inTwoParens :: Parser [Char] st (ListNumberStyle, Int) + -> Parser [Char] st ListAttributes inTwoParens num = try $ do char '(' (style, start) <- num @@ -443,9 +563,9 @@ inTwoParens num = try $ do -- | Parses an ordered list marker with a given style and delimiter, -- returns number. -orderedListMarker :: ListNumberStyle - -> ListNumberDelim - -> GenParser Char ParserState Int +orderedListMarker :: ListNumberStyle + -> ListNumberDelim + -> Parser [Char] ParserState Int orderedListMarker style delim = do let num = defaultNum <|> -- # can continue any kind of list case style of @@ -465,38 +585,51 @@ orderedListMarker style delim = do return start -- | Parses a character reference and returns a Str element. -charRef :: GenParser Char st Inline +charRef :: Parser [Char] st Inline charRef = do c <- characterReference return $ Str [c] +lineBlockLine :: Parser [Char] st String +lineBlockLine = try $ do + char '|' + char ' ' + white <- many (spaceChar >> return '\160') + notFollowedBy newline + line <- anyLine + continuations <- many (try $ char ' ' >> anyLine) + return $ white ++ unwords (line : continuations) + +-- | Parses an RST-style line block and returns a list of strings. +lineBlockLines :: Parser [Char] st [String] +lineBlockLines = try $ do + lines' <- many1 lineBlockLine + skipMany1 $ blankline <|> try (char '|' >> blankline) + return lines' + -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. -tableWith :: GenParser Char ParserState ([[Block]], [Alignment], [Int]) - -> ([Int] -> GenParser Char ParserState [[Block]]) - -> GenParser Char ParserState sep - -> GenParser Char ParserState end - -> GenParser Char ParserState [Inline] - -> GenParser Char ParserState Block -tableWith headerParser rowParser lineParser footerParser captionParser = try $ do - caption' <- option [] captionParser +tableWith :: Parser [Char] ParserState ([[Block]], [Alignment], [Int]) + -> ([Int] -> Parser [Char] ParserState [[Block]]) + -> Parser [Char] ParserState sep + -> Parser [Char] ParserState end + -> Parser [Char] ParserState Block +tableWith headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser - lines' <- rowParser indices `sepEndBy` lineParser + lines' <- rowParser indices `sepEndBy1` lineParser footerParser - caption <- if null caption' - then option [] captionParser - else return caption' - state <- getState - let numColumns = stateColumns state - let widths = widthsFromIndices numColumns indices - return $ Table caption aligns widths heads lines' + numColumns <- getOption readerColumns + let widths = if (indices == []) + then replicate (length aligns) 0.0 + else widthsFromIndices numColumns indices + return $ Table [] aligns widths heads lines' -- Calculate relative widths of table columns, based on indices widthsFromIndices :: Int -- Number of columns on terminal -> [Int] -- Indices -> [Double] -- Fractional relative sizes of columns -widthsFromIndices _ [] = [] -widthsFromIndices numColumns' indices = +widthsFromIndices _ [] = [] +widthsFromIndices numColumns' indices = let numColumns = max numColumns' (if null indices then 0 else last indices) lengths' = zipWith (-) indices (0:indices) lengths = reverse $ @@ -516,28 +649,30 @@ widthsFromIndices numColumns' indices = fracs = map (\l -> (fromIntegral l) / quotient) lengths in tail fracs +--- + -- 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). -gridTableWith :: GenParser Char ParserState Block -- ^ Block parser - -> GenParser Char ParserState [Inline] -- ^ Caption parser +gridTableWith :: Parser [Char] ParserState [Block] -- ^ Block list parser -> Bool -- ^ Headerless table - -> GenParser Char ParserState Block -gridTableWith block tableCaption headless = - tableWith (gridTableHeader headless block) (gridTableRow block) (gridTableSep '-') gridTableFooter tableCaption + -> Parser [Char] ParserState Block +gridTableWith blocks headless = + tableWith (gridTableHeader headless blocks) (gridTableRow blocks) + (gridTableSep '-') gridTableFooter gridTableSplitLine :: [Int] -> String -> [String] gridTableSplitLine indices line = map removeFinalBar $ tail $ - splitStringByIndices (init indices) $ removeTrailingSpace line + splitStringByIndices (init indices) $ trimr line -gridPart :: Char -> GenParser Char st (Int, Int) +gridPart :: Char -> Parser [Char] st (Int, Int) gridPart ch = do dashes <- many1 (char ch) char '+' return (length dashes, length dashes + 1) -gridDashedLines :: Char -> GenParser Char st [(Int,Int)] +gridDashedLines :: Char -> Parser [Char] st [(Int,Int)] gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) >>~ blankline removeFinalBar :: String -> String @@ -545,18 +680,18 @@ removeFinalBar = reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse -- | Separator between rows of grid table. -gridTableSep :: Char -> GenParser Char ParserState Char +gridTableSep :: Char -> Parser [Char] ParserState Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. gridTableHeader :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block - -> GenParser Char ParserState ([[Block]], [Alignment], [Int]) -gridTableHeader headless block = try $ do + -> Parser [Char] ParserState [Block] + -> Parser [Char] ParserState ([[Block]], [Alignment], [Int]) +gridTableHeader headless blocks = try $ do optional blanklines dashes <- gridDashedLines '-' rawContent <- if headless - then return $ repeat "" + then return $ repeat "" else many1 (notFollowedBy (gridTableSep '=') >> char '|' >> many1Till anyChar newline) @@ -571,25 +706,24 @@ gridTableHeader headless block = try $ do then replicate (length dashes) "" else map (intercalate " ") $ transpose $ map (gridTableSplitLine indices) rawContent - heads <- mapM (parseFromString $ many block) $ - map removeLeadingTrailingSpace rawHeads + heads <- mapM (parseFromString blocks) $ map trim rawHeads return (heads, aligns, indices) -gridTableRawLine :: [Int] -> GenParser Char ParserState [String] +gridTableRawLine :: [Int] -> Parser [Char] ParserState [String] gridTableRawLine indices = do char '|' line <- many1Till anyChar newline return (gridTableSplitLine indices line) -- | Parse row of grid table. -gridTableRow :: GenParser Char ParserState Block +gridTableRow :: Parser [Char] ParserState [Block] -> [Int] - -> GenParser Char ParserState [[Block]] -gridTableRow block indices = do + -> Parser [Char] ParserState [[Block]] +gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ transpose colLines - mapM (liftM compactifyCell . parseFromString (many block)) cols + mapM (liftM compactifyCell . parseFromString blocks) cols removeOneLeadingSpace :: [String] -> [String] removeOneLeadingSpace xs = @@ -603,23 +737,23 @@ compactifyCell :: [Block] -> [Block] compactifyCell bs = head $ compactify [bs] -- | Parse footer for a grid table. -gridTableFooter :: GenParser Char ParserState [Char] +gridTableFooter :: Parser [Char] ParserState [Char] gridTableFooter = blanklines --- -- | Parse a string with a given parser and state. -readWith :: GenParser t ParserState a -- ^ parser +readWith :: Parser [t] ParserState a -- ^ parser -> ParserState -- ^ initial state -> [t] -- ^ input -> a -readWith parser state input = +readWith parser state input = case runParser parser state "source" input of Left err' -> error $ "\nError:\n" ++ show err' Right result -> result -- | Parse a string with @parser@ (for testing). -testStringWith :: (Show a) => GenParser Char ParserState a +testStringWith :: (Show a) => Parser [Char] ParserState a -> String -> IO () testStringWith parser str = UTF8.putStrLn $ show $ @@ -627,72 +761,75 @@ testStringWith parser str = UTF8.putStrLn $ show $ -- | Parsing options. data ParserState = ParserState - { stateParseRaw :: Bool, -- ^ Parse raw HTML and LaTeX? + { stateOptions :: ReaderOptions, -- ^ User options stateParserContext :: ParserContext, -- ^ Inside list? stateQuoteContext :: QuoteContext, -- ^ Inside quoted environment? + stateAllowLinks :: Bool, -- ^ Allow parsing of links stateMaxNestingLevel :: Int, -- ^ Max # of nested Strong/Emph stateLastStrPos :: Maybe SourcePos, -- ^ Position after last str parsed - stateKeys :: KeyTable, -- ^ List of reference keys - stateCitations :: [String], -- ^ List of available citations - stateNotes :: NoteTable, -- ^ List of notes - stateTabStop :: Int, -- ^ Tab stop - stateStandalone :: Bool, -- ^ Parse bibliographic info? + stateKeys :: KeyTable, -- ^ List of reference keys (with fallbacks) + stateSubstitutions :: SubstTable, -- ^ List of substitution references + stateNotes :: NoteTable, -- ^ List of notes (raw bodies) + stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies) stateTitle :: [Inline], -- ^ Title of document stateAuthors :: [[Inline]], -- ^ Authors of document stateDate :: [Inline], -- ^ Date of document - stateStrict :: Bool, -- ^ Use strict markdown syntax? - stateSmart :: Bool, -- ^ Use smart typography? - stateOldDashes :: Bool, -- ^ Use pandoc <= 1.8.2.1 behavior - -- in parsing dashes; -- is em-dash; - -- before numeral is en-dash - stateLiterateHaskell :: Bool, -- ^ Treat input as literate haskell - stateColumns :: Int, -- ^ Number of columns in terminal stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used - stateIndentedCodeClasses :: [String], -- ^ Classes to use for indented code blocks + stateHeaders :: [[Inline]], -- ^ List of headers (used for implicit ref links) + stateIdentifiers :: [String], -- ^ List of header identifiers used stateNextExample :: Int, -- ^ Number of next example - stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers + stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers stateHasChapters :: Bool, -- ^ True if \chapter encountered - stateApplyMacros :: Bool, -- ^ Apply LaTeX macros? stateMacros :: [Macro], -- ^ List of macros defined so far - stateRstDefaultRole :: String -- ^ Current rST default interpreted text role + stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role + stateWarnings :: [String] -- ^ Warnings generated by the parser } - deriving Show + +instance Default ParserState where + def = defaultParserState defaultParserState :: ParserState -defaultParserState = - ParserState { stateParseRaw = False, +defaultParserState = + ParserState { stateOptions = def, stateParserContext = NullState, stateQuoteContext = NoQuote, + stateAllowLinks = True, stateMaxNestingLevel = 6, stateLastStrPos = Nothing, stateKeys = M.empty, - stateCitations = [], + stateSubstitutions = M.empty, stateNotes = [], - stateTabStop = 4, - stateStandalone = False, + stateNotes' = [], stateTitle = [], stateAuthors = [], stateDate = [], - stateStrict = False, - stateSmart = False, - stateOldDashes = False, - stateLiterateHaskell = False, - stateColumns = 80, stateHeaderTable = [], - stateIndentedCodeClasses = [], + stateHeaders = [], + stateIdentifiers = [], stateNextExample = 1, stateExamples = M.empty, stateHasChapters = False, - stateApplyMacros = True, stateMacros = [], - stateRstDefaultRole = "title-reference"} + stateRstDefaultRole = "title-reference", + stateWarnings = []} + +getOption :: (ReaderOptions -> a) -> Parser s ParserState a +getOption f = (f . stateOptions) `fmap` getState + +-- | Succeed only if the extension is enabled. +guardEnabled :: Extension -> Parser s ParserState () +guardEnabled ext = getOption readerExtensions >>= guard . Set.member ext -data HeaderType +-- | Succeed only if the extension is disabled. +guardDisabled :: Extension -> Parser s ParserState () +guardDisabled ext = getOption readerExtensions >>= guard . not . Set.member ext + +data HeaderType = SingleHeader Char -- ^ Single line of characters underneath | DoubleHeader Char -- ^ Lines of characters above and below deriving (Eq, Show) -data ParserContext +data ParserContext = ListItemState -- ^ Used when running parser on list item contents | NullState -- ^ Default state deriving (Eq, Show) @@ -705,51 +842,37 @@ data QuoteContext type NoteTable = [(String, String)] -newtype Key = Key [Inline] deriving (Show, Read, Eq, Ord) +type NoteTable' = [(String, F Blocks)] -- used in markdown reader -toKey :: [Inline] -> Key -toKey = Key . bottomUp lowercase - where lowercase :: Inline -> Inline - lowercase (Str xs) = Str (map toLower xs) - lowercase (Math t xs) = Math t (map toLower xs) - lowercase (Code attr xs) = Code attr (map toLower xs) - lowercase (RawInline f xs) = RawInline f (map toLower xs) - lowercase LineBreak = Space - lowercase x = x +newtype Key = Key String deriving (Show, Read, Eq, Ord) -fromKey :: Key -> [Inline] -fromKey (Key xs) = xs +toKey :: String -> Key +toKey = Key . map toLower . unwords . words type KeyTable = M.Map Key Target --- | Look up key in key table and return target object. -lookupKeySrc :: KeyTable -- ^ Key table - -> Key -- ^ Key - -> Maybe Target -lookupKeySrc table key = case M.lookup key table of - Nothing -> Nothing - Just src -> Just src +type SubstTable = M.Map Key Inlines -- | Fail unless we're in "smart typography" mode. -failUnlessSmart :: GenParser tok ParserState () -failUnlessSmart = getState >>= guard . stateSmart +failUnlessSmart :: Parser [tok] ParserState () +failUnlessSmart = getOption readerSmart >>= guard -smartPunctuation :: GenParser Char ParserState Inline - -> GenParser Char ParserState Inline +smartPunctuation :: Parser [Char] ParserState Inline + -> Parser [Char] ParserState Inline smartPunctuation inlineParser = do failUnlessSmart choice [ quoted inlineParser, apostrophe, dash, ellipses ] -apostrophe :: GenParser Char ParserState Inline +apostrophe :: Parser [Char] ParserState Inline apostrophe = (char '\'' <|> char '\8217') >> return (Str "\x2019") -quoted :: GenParser Char ParserState Inline - -> GenParser Char ParserState Inline +quoted :: Parser [Char] ParserState Inline + -> Parser [Char] ParserState Inline quoted inlineParser = doubleQuoted inlineParser <|> singleQuoted inlineParser withQuoteContext :: QuoteContext - -> (GenParser Char ParserState Inline) - -> GenParser Char ParserState Inline + -> Parser [tok] ParserState a + -> Parser [tok] ParserState a withQuoteContext context parser = do oldState <- getState let oldQuoteContext = stateQuoteContext oldState @@ -759,128 +882,137 @@ withQuoteContext context parser = do setState newState { stateQuoteContext = oldQuoteContext } return result -singleQuoted :: GenParser Char ParserState Inline - -> GenParser Char ParserState Inline +singleQuoted :: Parser [Char] ParserState Inline + -> Parser [Char] ParserState Inline singleQuoted inlineParser = try $ do singleQuoteStart withQuoteContext InSingleQuote $ many1Till inlineParser singleQuoteEnd >>= return . Quoted SingleQuote . normalizeSpaces -doubleQuoted :: GenParser Char ParserState Inline - -> GenParser Char ParserState Inline +doubleQuoted :: Parser [Char] ParserState Inline + -> Parser [Char] ParserState Inline doubleQuoted inlineParser = try $ do doubleQuoteStart withQuoteContext InDoubleQuote $ do contents <- manyTill inlineParser doubleQuoteEnd return . Quoted DoubleQuote . normalizeSpaces $ contents -failIfInQuoteContext :: QuoteContext -> GenParser tok ParserState () +failIfInQuoteContext :: QuoteContext -> Parser [tok] ParserState () failIfInQuoteContext context = do st <- getState if stateQuoteContext st == context then fail "already inside quotes" else return () -charOrRef :: [Char] -> GenParser Char st Char +charOrRef :: [Char] -> Parser [Char] st Char charOrRef cs = oneOf cs <|> try (do c <- characterReference guard (c `elem` cs) return c) -updateLastStrPos :: GenParser Char ParserState () -updateLastStrPos = getPosition >>= \p -> +updateLastStrPos :: Parser [Char] ParserState () +updateLastStrPos = getPosition >>= \p -> updateState $ \s -> s{ stateLastStrPos = Just p } -singleQuoteStart :: GenParser Char ParserState () +singleQuoteStart :: Parser [Char] ParserState () singleQuoteStart = do failIfInQuoteContext InSingleQuote pos <- getPosition st <- getState -- single quote start can't be right after str guard $ stateLastStrPos st /= Just pos - try $ do charOrRef "'\8216\145" - notFollowedBy (oneOf ")!],;:-? \t\n") - notFollowedBy (char '.') <|> lookAhead (string "..." >> return ()) - notFollowedBy (try (oneOfStrings ["s","t","m","ve","ll","re"] >> - satisfy (not . isAlphaNum))) - -- possess/contraction - return () - -singleQuoteEnd :: GenParser Char st () + () <$ charOrRef "'\8216\145" + +singleQuoteEnd :: Parser [Char] st () singleQuoteEnd = try $ do charOrRef "'\8217\146" notFollowedBy alphaNum -doubleQuoteStart :: GenParser Char ParserState () +doubleQuoteStart :: Parser [Char] ParserState () doubleQuoteStart = do failIfInQuoteContext InDoubleQuote try $ do charOrRef "\"\8220\147" notFollowedBy (satisfy (\c -> c == ' ' || c == '\t' || c == '\n')) -doubleQuoteEnd :: GenParser Char st () +doubleQuoteEnd :: Parser [Char] st () doubleQuoteEnd = do charOrRef "\"\8221\148" return () -ellipses :: GenParser Char st Inline +ellipses :: Parser [Char] st Inline ellipses = do try (charOrRef "\8230\133") <|> try (string "..." >> return '…') return (Str "\8230") -dash :: GenParser Char ParserState Inline +dash :: Parser [Char] ParserState Inline dash = do - oldDashes <- stateOldDashes `fmap` getState + oldDashes <- getOption readerOldDashes if oldDashes then emDashOld <|> enDashOld else Str `fmap` (hyphenDash <|> emDash <|> enDash) -- Two hyphens = en-dash, three = em-dash -hyphenDash :: GenParser Char st String +hyphenDash :: Parser [Char] st String hyphenDash = do try $ string "--" option "\8211" (char '-' >> return "\8212") -emDash :: GenParser Char st String +emDash :: Parser [Char] st String emDash = do try (charOrRef "\8212\151") return "\8212" -enDash :: GenParser Char st String +enDash :: Parser [Char] st String enDash = do try (charOrRef "\8212\151") return "\8211" -enDashOld :: GenParser Char st Inline +enDashOld :: Parser [Char] st Inline enDashOld = do try (charOrRef "\8211\150") <|> try (char '-' >> lookAhead (satisfy isDigit) >> return '–') return (Str "\8211") -emDashOld :: GenParser Char st Inline +emDashOld :: Parser [Char] st Inline emDashOld = do try (charOrRef "\8212\151") <|> (try $ string "--" >> optional (char '-') >> return '-') return (Str "\8212") +-- This is used to prevent exponential blowups for things like: +-- a**a*a**a*a**a*a**a*a**a*a**a*a** +nested :: Parser s ParserState a + -> Parser s ParserState a +nested p = do + nestlevel <- stateMaxNestingLevel `fmap` getState + guard $ nestlevel > 0 + updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 } + res <- p + updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } + return res + -- -- Macros -- -- | Parse a \newcommand or \renewcommand macro definition. -macro :: GenParser Char ParserState Block +macro :: Parser [Char] ParserState Block macro = do - getState >>= guard . stateApplyMacros + apply <- getOption readerApplyMacros inp <- getInput case parseMacroDefinitions inp of - ([], _) -> pzero - (ms, rest) -> do count (length inp - length rest) anyChar - updateState $ \st -> - st { stateMacros = ms ++ stateMacros st } - return Null + ([], _) -> mzero + (ms, rest) -> do def' <- count (length inp - length rest) anyChar + if apply + then do + updateState $ \st -> + st { stateMacros = ms ++ stateMacros st } + return Null + else return $ RawBlock "latex" def' -- | Apply current macros to string. -applyMacros' :: String -> GenParser Char ParserState String +applyMacros' :: String -> Parser [Char] ParserState String applyMacros' target = do - apply <- liftM stateApplyMacros getState + apply <- getOption readerApplyMacros if apply then do macros <- liftM stateMacros getState return $ applyMacros macros target diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index bf78b2594..211fdf20e 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111(-1)307 USA {- | Module : Text.Pandoc.Pretty Copyright : Copyright (C) 2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -202,18 +202,17 @@ chomp d = Doc (fromList dl') outp :: (IsString a, Monoid a) => Int -> String -> DocState a -outp off s | off <= 0 = do +outp off s | off < 0 = do -- offset < 0 means newline characters st' <- get let rawpref = prefix st' when (column st' == 0 && usePrefix st' && not (null rawpref)) $ do let pref = reverse $ dropWhile isSpace $ reverse rawpref modify $ \st -> st{ output = fromString pref : output st , column = column st + realLength pref } - when (off < 0) $ do - modify $ \st -> st { output = fromString s : output st - , column = 0 - , newlines = newlines st + 1 } -outp off s = do + modify $ \st -> st { output = fromString s : output st + , column = 0 + , newlines = newlines st + 1 } +outp off s = do -- offset >= 0 (0 might be combining char) st' <- get let pref = prefix st' when (column st' == 0 && usePrefix st' && not (null pref)) $ do @@ -510,7 +509,9 @@ charWidth c = | c >= '\xFE10' && c <= '\xFE19' -> 2 | c >= '\xFE20' && c <= '\xFE26' -> 1 | c >= '\xFE30' && c <= '\xFE6B' -> 2 - | c >= '\xFE70' && c <= '\x16A38' -> 1 + | c >= '\xFE70' && c <= '\xFEFF' -> 1 + | c >= '\xFF01' && c <= '\xFF60' -> 2 + | c >= '\xFF61' && c <= '\x16A38' -> 1 | c >= '\x1B000' && c <= '\x1B001' -> 2 | c >= '\x1D000' && c <= '\x1F1FF' -> 1 | c >= '\x1F200' && c <= '\x1F251' -> 2 diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 62f7c61a0..0058e889c 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1,6 +1,6 @@ module Text.Pandoc.Readers.DocBook ( readDocBook ) where import Data.Char (toUpper, isDigit) -import Text.Pandoc.Parsing (ParserState(..)) +import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Builder import Text.XML.Light @@ -133,7 +133,7 @@ List of all DocBook tags, with [x] indicating implemented, [ ] exceptionname - The name of an exception [ ] fax - A fax number [ ] fieldsynopsis - The name of a field in a class definition -[ ] figure - A formal figure, generally an illustration, with a title +[x] figure - A formal figure, generally an illustration, with a title [x] filename - The name of a file [ ] firstname - The first name of a person [ ] firstterm - The first occurrence of a term @@ -455,13 +455,13 @@ List of all DocBook tags, with [x] indicating implemented, [x] tocfront - An entry in a table of contents for a front matter component [x] toclevel1 - A top-level entry within a table of contents entry for a chapter-like component -[x] toclevel2 - A second-level entry within a table of contents entry for a +[x] toclevel2 - A second-level entry within a table of contents entry for a chapter-like component -[x] toclevel3 - A third-level entry within a table of contents entry for a +[x] toclevel3 - A third-level entry within a table of contents entry for a chapter-like component -[x] toclevel4 - A fourth-level entry within a table of contents entry for a +[x] toclevel4 - A fourth-level entry within a table of contents entry for a chapter-like component -[x] toclevel5 - A fifth-level entry within a table of contents entry for a +[x] toclevel5 - A fifth-level entry within a table of contents entry for a chapter-like component [x] tocpart - An entry in a table of contents for a part of a book [ ] token - A unit of information @@ -501,9 +501,10 @@ data DBState = DBState{ dbSectionLevel :: Int , dbDocAuthors :: [Inlines] , dbDocDate :: Inlines , dbBook :: Bool + , dbFigureTitle :: Inlines } deriving Show -readDocBook :: ParserState -> String -> Pandoc +readDocBook :: ReaderOptions -> String -> Pandoc readDocBook _ inp = setTitle (dbDocTitle st') $ setAuthors (dbDocAuthors st') $ setDate (dbDocDate st') @@ -515,8 +516,19 @@ readDocBook _ inp = setTitle (dbDocTitle st') , dbDocAuthors = [] , dbDocDate = mempty , dbBook = False + , dbFigureTitle = mempty } +getFigure :: Element -> DB Blocks +getFigure e = do + tit <- case filterChild (named "title") e of + Just t -> getInlines t + Nothing -> return mempty + modify $ \st -> st{ dbFigureTitle = tit } + res <- getBlocks e + modify $ \st -> st{ dbFigureTitle = mempty } + return res + -- normalize input, consolidating adjacent Text and CRef elements normalizeTree :: [Content] -> [Content] normalizeTree = everywhere (mkT go) @@ -574,7 +586,7 @@ addToStart toadd bs = (Para xs : rest) -> para (toadd <> fromList xs) <> fromList rest _ -> bs --- function that is used by both mediaobject (in parseBlock) +-- function that is used by both mediaobject (in parseBlock) -- and inlinemediaobject (in parseInline) getImage :: Element -> DB Inlines getImage e = do @@ -585,10 +597,13 @@ getImage e = do Just i -> return $ attrValue "fileref" i caption <- case filterChild (\x -> named "caption" x || named "textobject" x) e of - Nothing -> return mempty + Nothing -> gets dbFigureTitle Just z -> mconcat <$> (mapM parseInline $ elContent z) return $ image imageUrl "" caption +getBlocks :: Element -> DB Blocks +getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) + parseBlock :: Content -> DB Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE parseBlock (Text (CData _ s _)) = if all isSpace s @@ -613,7 +628,7 @@ parseBlock (Elem e) = "attribution" -> return mempty "titleabbrev" -> return mempty "authorinitials" -> return mempty - "title" -> return mempty -- handled by getTitle or sect + "title" -> return mempty -- handled by getTitle or sect or figure "bibliography" -> sect 0 "bibliodiv" -> sect 1 "biblioentry" -> parseMixed para (elContent e) @@ -674,7 +689,8 @@ parseBlock (Elem e) = orderedListWith (start,listStyle,DefaultDelim) <$> listitems "variablelist" -> definitionList <$> deflistitems - "mediaobject" -> para <$> (getImage e) + "figure" -> getFigure e + "mediaobject" -> para <$> getImage e "caption" -> return mempty "info" -> getTitle >> getAuthors >> getDate >> return mempty "articleinfo" -> getTitle >> getAuthors >> getDate >> return mempty @@ -702,8 +718,7 @@ parseBlock (Elem e) = "programlisting" -> codeBlockWithLang "?xml" -> return mempty _ -> getBlocks e - where getBlocks e' = mconcat <$> (mapM parseBlock $ elContent e') - parseMixed container conts = do + 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' @@ -862,15 +877,15 @@ parseInline (Elem e) = "varargs" -> return $ code "(...)" "xref" -> return $ str "?" -- so at least you know something is there "email" -> return $ link ("mailto:" ++ strContent e) "" - $ code $ strContent e - "uri" -> return $ link (strContent e) "" $ code $ strContent e + $ str $ strContent e + "uri" -> return $ link (strContent e) "" $ str $ strContent e "ulink" -> link (attrValue "url" e) "" <$> innerInlines "link" -> do ils <- innerInlines let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of Just h -> h _ -> ('#' : attrValue "linkend" e) - let ils' = if ils == mempty then code href else ils + let ils' = if ils == mempty then str href else ils return $ link href "" ils' "foreignphrase" -> emph <$> innerInlines "emphasis" -> case attrValue "role" e of diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 536bddd39..76bce9971 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -19,10 +19,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.HTML Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha + Stability : alpha Portability : portable Conversion of HTML to 'Pandoc' document. @@ -36,18 +36,17 @@ module Text.Pandoc.Readers.HTML ( readHtml , isCommentTag ) where -import Text.ParserCombinators.Parsec -import Text.ParserCombinators.Parsec.Pos import Text.HTML.TagSoup import Text.HTML.TagSoup.Match import Text.Pandoc.Definition import Text.Pandoc.Builder (text, toList) import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Parsing import Data.Maybe ( fromMaybe, isJust ) import Data.List ( intercalate ) -import Data.Char ( isDigit, toLower ) -import Control.Monad ( liftM, guard, when ) +import Data.Char ( isDigit ) +import Control.Monad ( liftM, guard, when, mzero ) isSpace :: Char -> Bool isSpace ' ' = True @@ -56,11 +55,11 @@ isSpace '\n' = True isSpace _ = False -- | Convert HTML-formatted string to 'Pandoc' document. -readHtml :: ParserState -- ^ Parser state +readHtml :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assumes @'\n'@ line endings) -> Pandoc -readHtml st inp = Pandoc meta blocks - where blocks = readWith parseBody st rest +readHtml opts inp = Pandoc meta blocks + where blocks = readWith parseBody def{ stateOptions = opts } rest tags = canonicalizeTags $ parseTagsOptions parseOptions{ optTagPosition = True } inp hasHeader = any (~== TagOpen "head" []) tags @@ -68,7 +67,7 @@ readHtml st inp = Pandoc meta blocks then parseHeader tags else (Meta [] [] [], tags) -type TagParser = GenParser (Tag String) ParserState +type TagParser = Parser [Tag String] ParserState -- TODO - fix this - not every header has a title tag parseHeader :: [Tag String] -> (Meta, [Tag String]) @@ -96,18 +95,6 @@ block = choice , pRawHtmlBlock ] --- repeated in SelfContained -- consolidate eventually -renderTags' :: [Tag String] -> String -renderTags' = renderTagsOptions - renderOptions{ optMinimize = \x -> - let y = map toLower x - in y == "hr" || y == "br" || - y == "img" || y == "meta" || - y == "link" - , optRawTag = \x -> - let y = map toLower x - in y == "script" || y == "style" } - pList :: TagParser [Block] pList = pBulletList <|> pOrderedList <|> pDefinitionList @@ -126,25 +113,22 @@ pBulletList = try $ do pOrderedList :: TagParser [Block] pOrderedList = try $ do TagOpen _ attribs <- pSatisfy (~== TagOpen "ol" []) - st <- getState - let (start, style) = if stateStrict st - then (1, DefaultStyle) - else (sta', sty') - where sta = fromMaybe "1" $ - lookup "start" attribs - sta' = if all isDigit sta - then read sta - else 1 - sty = fromMaybe (fromMaybe "" $ - lookup "style" attribs) $ - lookup "class" attribs - sty' = case sty of - "lower-roman" -> LowerRoman - "upper-roman" -> UpperRoman - "lower-alpha" -> LowerAlpha - "upper-alpha" -> UpperAlpha - "decimal" -> Decimal - _ -> DefaultStyle + let (start, style) = (sta', sty') + where sta = fromMaybe "1" $ + lookup "start" attribs + sta' = if all isDigit sta + then read sta + else 1 + sty = fromMaybe (fromMaybe "" $ + lookup "style" attribs) $ + lookup "class" attribs + sty' = case sty of + "lower-roman" -> LowerRoman + "upper-roman" -> UpperRoman + "lower-alpha" -> LowerAlpha + "upper-alpha" -> UpperAlpha + "decimal" -> Decimal + _ -> DefaultStyle let nonItem = pSatisfy (\t -> not (tagOpen (`elem` ["li","ol","ul","dl"]) (const True) t) && not (t ~== TagClose "ol")) @@ -176,7 +160,7 @@ fixPlains inList bs = if any isParaish bs else bs where isParaish (Para _) = True isParaish (CodeBlock _ _) = True - isParaish (Header _ _) = True + isParaish (Header _ _ _) = True isParaish (BlockQuote _) = True isParaish (BulletList _) = not inList isParaish (OrderedList _ _) = not inList @@ -196,8 +180,8 @@ pRawTag = do pRawHtmlBlock :: TagParser [Block] pRawHtmlBlock = do raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag - state <- getState - if stateParseRaw state && not (null raw) + parseRaw <- getOption readerParseRaw + if parseRaw && not (null raw) then return [RawBlock "html" raw] else return [] @@ -217,7 +201,9 @@ pHeader = try $ do contents <- liftM concat $ manyTill inline (pCloses tagtype <|> eof) return $ if bodyTitle then [] -- skip a representation of the title in the body - else [Header level $ normalizeSpaces contents] + else [Header level (fromAttrib "id" $ + TagOpen tagtype attr, [], []) $ + normalizeSpaces contents] pHrule :: TagParser [Block] pHrule = do @@ -235,7 +221,7 @@ pSimpleTable = try $ do rows <- pOptInTag "tbody" $ many1 $ try $ skipMany pBlank >> pInTags "tr" (pCell "td") skipMany pBlank - TagClose _ <- pSatisfy (~== TagClose "table") + TagClose _ <- pSatisfy (~== TagClose "table") let cols = maximum $ map length rows let aligns = replicate cols AlignLeft let widths = replicate cols 0 @@ -281,15 +267,13 @@ pCodeBlock = try $ do let attribsId = fromMaybe "" $ lookup "id" attr let attribsClasses = words $ fromMaybe "" $ lookup "class" attr let attribsKV = filter (\(k,_) -> k /= "class" && k /= "id") attr - st <- getState - let attribs = if stateStrict st - then ("",[],[]) - else (attribsId, attribsClasses, attribsKV) + let attribs = (attribsId, attribsClasses, attribsKV) return [CodeBlock attribs result] inline :: TagParser [Inline] inline = choice [ pTagText + , pQ , pEmph , pStrong , pSuperscript @@ -310,7 +294,7 @@ pLocation = do pSat :: (Tag String -> Bool) -> TagParser (Tag String) pSat f = do pos <- getPosition - token show (const pos) (\x -> if f x then Just x else Nothing) + token show (const pos) (\x -> if f x then Just x else Nothing) pSatisfy :: (Tag String -> Bool) -> TagParser (Tag String) pSatisfy f = try $ optional pLocation >> pSat f @@ -325,6 +309,17 @@ pSelfClosing f g = do optional $ pSatisfy (tagClose f) return open +pQ :: TagParser [Inline] +pQ = do + quoteContext <- stateQuoteContext `fmap` getState + let quoteType = case quoteContext of + InDoubleQuote -> SingleQuote + _ -> DoubleQuote + let innerQuoteContext = if quoteType == SingleQuote + then InSingleQuote + else InDoubleQuote + withQuoteContext innerQuoteContext $ pInlinesInTags "q" (Quoted quoteType) + pEmph :: TagParser [Inline] pEmph = pInlinesInTags "em" Emph <|> pInlinesInTags "i" Emph @@ -332,14 +327,13 @@ pStrong :: TagParser [Inline] pStrong = pInlinesInTags "strong" Strong <|> pInlinesInTags "b" Strong pSuperscript :: TagParser [Inline] -pSuperscript = failIfStrict >> pInlinesInTags "sup" Superscript +pSuperscript = pInlinesInTags "sup" Superscript pSubscript :: TagParser [Inline] -pSubscript = failIfStrict >> pInlinesInTags "sub" Subscript +pSubscript = pInlinesInTags "sub" Subscript pStrikeout :: TagParser [Inline] pStrikeout = do - failIfStrict pInlinesInTags "s" Strikeout <|> pInlinesInTags "strike" Strikeout <|> pInlinesInTags "del" Strikeout <|> @@ -381,8 +375,8 @@ pCode = try $ do pRawHtmlInline :: TagParser [Inline] pRawHtmlInline = do result <- pSatisfy (tagComment (const True)) <|> pSatisfy isInlineTag - state <- getState - if stateParseRaw state + parseRaw <- getOption readerParseRaw + if parseRaw then return [RawInline "html" $ renderTags' [result]] else return [] @@ -417,7 +411,7 @@ pCloses tagtype = try $ do (TagClose "ul") | tagtype == "li" -> return () (TagClose "ol") | tagtype == "li" -> return () (TagClose "dl") | tagtype == "li" -> return () - _ -> pzero + _ -> mzero pTagText :: TagParser [Inline] pTagText = try $ do @@ -432,11 +426,11 @@ pBlank = try $ do (TagText str) <- pSatisfy isTagText guard $ all isSpace str -pTagContents :: GenParser Char ParserState Inline +pTagContents :: Parser [Char] ParserState Inline pTagContents = pStr <|> pSpace <|> smartPunctuation pTagContents <|> pSymbol <|> pBad -pStr :: GenParser Char ParserState Inline +pStr :: Parser [Char] ParserState Inline pStr = do result <- many1 $ satisfy $ \c -> not (isSpace c) && not (isSpecial c) && not (isBad c) @@ -455,13 +449,13 @@ isSpecial '\8220' = True isSpecial '\8221' = True isSpecial _ = False -pSymbol :: GenParser Char ParserState Inline +pSymbol :: Parser [Char] ParserState Inline pSymbol = satisfy isSpecial >>= return . Str . (:[]) isBad :: Char -> Bool isBad c = c >= '\128' && c <= '\159' -- not allowed in HTML -pBad :: GenParser Char ParserState Inline +pBad :: Parser [Char] ParserState Inline pBad = do c <- satisfy isBad let c' = case c of @@ -495,7 +489,7 @@ pBad = do _ -> '?' return $ Str [c'] -pSpace :: GenParser Char ParserState Inline +pSpace :: Parser [Char] ParserState Inline pSpace = many1 (satisfy isSpace) >> return Space -- @@ -516,12 +510,15 @@ inlineHtmlTags = ["a", "abbr", "acronym", "b", "basefont", "bdo", "big", -} blockHtmlTags :: [String] -blockHtmlTags = ["address", "blockquote", "body", "center", "dir", "div", - "dl", "fieldset", "form", "h1", "h2", "h3", "h4", - "h5", "h6", "head", "hr", "html", "isindex", "menu", - "noframes", "noscript", "ol", "p", "pre", "table", "ul", "dd", +blockHtmlTags = ["address", "article", "aside", "blockquote", "body", "button", "canvas", + "caption", "center", "col", "colgroup", "dd", "dir", "div", + "dl", "dt", "embed", "fieldset", "figcaption", "figure", "footer", + "form", "h1", "h2", "h3", "h4", + "h5", "h6", "head", "header", "hgroup", "hr", "html", "isindex", "map", "menu", + "noframes", "noscript", "object", "ol", "output", "p", "pre", "progress", + "section", "table", "tbody", "textarea", "thead", "tfoot", "ul", "dd", "dt", "frameset", "li", "tbody", "td", "tfoot", - "th", "thead", "tr", "script", "style"] + "th", "thead", "tr", "script", "style", "video"] -- We want to allow raw docbook in markdown documents, so we -- include docbook block tags here too. @@ -593,22 +590,21 @@ _ `closes` _ = False --- parsers for use in markdown, textile readers -- | Matches a stretch of HTML in balanced tags. -htmlInBalanced :: (Tag String -> Bool) -> GenParser Char ParserState String +htmlInBalanced :: (Tag String -> Bool) -> Parser [Char] ParserState String htmlInBalanced f = try $ do (TagOpen t _, tag) <- htmlTag f guard $ '/' `notElem` tag -- not a self-closing tag - let nonTagChunk = many1 $ satisfy (/= '<') let stopper = htmlTag (~== TagClose t) let anytag = liftM snd $ htmlTag (const True) contents <- many $ notFollowedBy' stopper >> - (nonTagChunk <|> htmlInBalanced (const True) <|> anytag) + (htmlInBalanced f <|> anytag <|> count 1 anyChar) endtag <- liftM snd stopper return $ tag ++ concat contents ++ endtag -- | Matches a tag meeting a certain condition. -htmlTag :: (Tag String -> Bool) -> GenParser Char ParserState (Tag String, String) +htmlTag :: (Tag String -> Bool) -> Parser [Char] st (Tag String, String) htmlTag f = try $ do - lookAhead (char '<') + lookAhead $ char '<' >> (oneOf "/!?" <|> letter) (next : _) <- getInput >>= return . canonicalizeTags . parseTags guard $ f next -- advance the parser @@ -617,7 +613,7 @@ htmlTag f = try $ do count (length s + 4) anyChar skipMany (satisfy (/='>')) char '>' - return (next, "<!--" ++ s ++ "-->") + return (next, "<!--" ++ s ++ "-->") _ -> do rendered <- manyTill anyChar (char '>') return (next, rendered ++ ">") diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 37f34e853..5362b1b53 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2006-2012 John MacFarlane <jgm@berkeley.edu> @@ -27,17 +28,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of LaTeX to 'Pandoc' document. -} -{-# LANGUAGE ScopedTypeVariables #-} module Text.Pandoc.Readers.LaTeX ( readLaTeX, rawLaTeXInline, rawLaTeXBlock, handleIncludes ) where -import Text.ParserCombinators.Parsec hiding ((<|>), space, many, optional) import Text.Pandoc.Definition import Text.Pandoc.Shared -import Text.Pandoc.Parsing +import Text.Pandoc.Options +import Text.Pandoc.Biblio (processBiblio) +import Text.Pandoc.Parsing hiding ((<|>), many, optional, space) import qualified Text.Pandoc.UTF8 as UTF8 import Data.Char ( chr, ord ) import Control.Monad @@ -45,16 +46,17 @@ import Text.Pandoc.Builder import Data.Char (isLetter, isPunctuation, isSpace) import Control.Applicative import Data.Monoid -import System.FilePath (replaceExtension) +import System.Environment (getEnv) +import System.FilePath (replaceExtension, (</>)) import Data.List (intercalate) import qualified Data.Map as M -import qualified Control.Exception as E (catch, IOException) +import qualified Control.Exception as E -- | Parse LaTeX from string and return 'Pandoc' document. -readLaTeX :: ParserState -- ^ Parser state, including options for parser +readLaTeX :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assumes @'\n'@ line endings) -> Pandoc -readLaTeX = readWith parseLaTeX +readLaTeX opts = readWith parseLaTeX def{ stateOptions = opts } parseLaTeX :: LP Pandoc parseLaTeX = do @@ -64,9 +66,12 @@ parseLaTeX = do let title' = stateTitle st let authors' = stateAuthors st let date' = stateDate st - return $ Pandoc (Meta title' authors' date') $ toList bs + refs <- getOption readerReferences + mbsty <- getOption readerCitationStyle + return $ processBiblio mbsty refs + $ Pandoc (Meta title' authors' date') $ toList bs -type LP = GenParser Char ParserState +type LP = Parser [Char] ParserState anyControlSeq :: LP String anyControlSeq = do @@ -147,9 +152,6 @@ braced = bgroup *> (concat <$> manyTill bracketed :: Monoid a => LP a -> LP a bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']')) -trim :: String -> String -trim = removeLeadingTrailingSpace - mathDisplay :: LP String -> LP Inlines mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim) @@ -167,10 +169,8 @@ double_quote = (doubleQuoted . mconcat) <$> (try $ string "``" *> manyTill inline (try $ string "''")) single_quote :: LP Inlines -single_quote = char '`' *> - ( try ((singleQuoted . mconcat) <$> - manyTill inline (try $ char '\'' >> notFollowedBy letter)) - <|> lit "`") +single_quote = (singleQuoted . mconcat) <$> + (try $ char '`' *> manyTill inline (try $ char '\'' >> notFollowedBy letter)) inline :: LP Inlines inline = (mempty <$ comment) @@ -182,17 +182,19 @@ inline = (mempty <$ comment) ((char '-') *> option (str "–") (str "—" <$ char '-'))) <|> double_quote <|> single_quote + <|> (str "“" <$ try (string "``")) -- nb. {``} won't be caught by double_quote + <|> (str "”" <$ try (string "''")) + <|> (str "‘" <$ char '`') -- nb. {`} won't be caught by single_quote <|> (str "’" <$ char '\'') <|> (str "\160" <$ char '~') <|> (mathDisplay $ string "$$" *> mathChars <* string "$$") <|> (mathInline $ char '$' *> mathChars <* char '$') <|> (superscript <$> (char '^' *> tok)) <|> (subscript <$> (char '_' *> tok)) - <|> (failUnlessLHS *> char '|' *> doLHSverb) - <|> (str <$> count 1 tildeEscape) - <|> (str <$> string "]") - <|> (str <$> string "#") -- TODO print warning? - <|> (str <$> string "&") -- TODO print warning? + <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb) + <|> (str . (:[]) <$> tildeEscape) + <|> (str . (:[]) <$> oneOf "[]") + <|> (str . (:[]) <$> oneOf "#&") -- TODO print warning? -- <|> (str <$> count 1 (satisfy (\c -> c /= '\\' && c /='\n' && c /='}' && c /='{'))) -- eat random leftover characters inlines :: LP Inlines @@ -202,10 +204,10 @@ block :: LP Blocks block = (mempty <$ comment) <|> (mempty <$ ((spaceChar <|> newline) *> spaces)) <|> environment - <|> mempty <$ macro -- TODO improve macros, make them work everywhere + <|> mempty <$ macro <|> blockCommand - <|> grouped block <|> paragraph + <|> grouped block <|> (mempty <$ char '&') -- loose & in table environment @@ -215,6 +217,7 @@ blocks = mconcat <$> many block blockCommand :: LP Blocks blockCommand = try $ do name <- anyControlSeq + guard $ name /= "begin" && name /= "end" star <- option "" (string "*" <* optional sp) let name' = name ++ star case M.lookup name' blockCommands of @@ -232,14 +235,14 @@ ignoreInlines name = (name, doraw <|> (mempty <$ optargs)) where optargs = skipopts *> skipMany (try $ optional sp *> braced) contseq = '\\':name doraw = (rawInline "latex" . (contseq ++) . snd) <$> - (getState >>= guard . stateParseRaw >> (withRaw optargs)) + (getOption readerParseRaw >>= guard >> (withRaw optargs)) ignoreBlocks :: String -> (String, LP Blocks) ignoreBlocks name = (name, doraw <|> (mempty <$ optargs)) where optargs = skipopts *> skipMany (try $ optional sp *> braced) contseq = '\\':name doraw = (rawBlock "latex" . (contseq ++) . snd) <$> - (getState >>= guard . stateParseRaw >> (withRaw optargs)) + (getOption readerParseRaw >>= guard >> (withRaw optargs)) blockCommands :: M.Map String (LP Blocks) blockCommands = M.fromList $ @@ -266,8 +269,6 @@ blockCommands = M.fromList $ , ("closing", skipopts *> closing) -- , ("rule", skipopts *> tok *> tok *> pure horizontalRule) - , ("begin", mzero) -- these are here so they won't be interpreted as inline - , ("end", mzero) , ("item", skipopts *> loose_item) , ("documentclass", skipopts *> braced *> preamble) , ("centerline", (para . trimInlines) <$> (skipopts *> tok)) @@ -285,7 +286,6 @@ blockCommands = M.fromList $ -- that are to be processed by the compiler but not printed. , "ignore" , "hyperdef" - , "noindent" , "markboth", "markright", "markleft" , "hspace", "vspace" ] @@ -322,15 +322,20 @@ section lvl = do inlineCommand :: LP Inlines inlineCommand = try $ do name <- anyControlSeq + guard $ name /= "begin" && name /= "end" guard $ not $ isBlockCommand name - parseRaw <- stateParseRaw `fmap` getState + parseRaw <- getOption readerParseRaw star <- option "" (string "*") let name' = name ++ star - let rawargs = withRaw (skipopts *> option "" dimenarg - *> many braced) >>= applyMacros' . snd - let raw = if parseRaw - then (rawInline "latex" . (('\\':name') ++)) <$> rawargs - else mempty <$> rawargs + let raw = do + rawargs <- withRaw (skipopts *> option "" dimenarg *> many braced) + let rawcommand = '\\' : name ++ star ++ snd rawargs + transformed <- applyMacros' rawcommand + if transformed /= rawcommand + then parseFromString inlines transformed + else if parseRaw + then return $ rawInline "latex" rawcommand + else return mempty case M.lookup name' inlineCommands of Just p -> p <|> raw Nothing -> case M.lookup name inlineCommands of @@ -338,7 +343,7 @@ inlineCommand = try $ do Nothing -> raw unlessParseRaw :: LP () -unlessParseRaw = getState >>= guard . not . stateParseRaw +unlessParseRaw = getOption readerParseRaw >>= guard . not isBlockCommand :: String -> Bool isBlockCommand s = maybe False (const True) $ M.lookup s blockCommands @@ -353,6 +358,7 @@ inlineCommands = M.fromList $ , ("textsubscript", subscript <$> tok) , ("textbackslash", lit "\\") , ("backslash", lit "\\") + , ("slash", lit "/") , ("textbf", strong <$> tok) , ("ldots", lit "…") , ("dots", lit "…") @@ -420,23 +426,24 @@ inlineCommands = M.fromList $ , ("lstinline", doverb) , ("texttt", (code . stringify . toList) <$> tok) , ("url", (unescapeURL <$> braced) >>= \url -> - pure (link url "" (codeWith ("",["url"],[]) url))) + pure (link url "" (str url))) , ("href", (unescapeURL <$> braced <* optional sp) >>= \url -> tok >>= \lab -> pure (link url "" lab)) , ("includegraphics", skipopts *> (unescapeURL <$> braced) >>= (\src -> pure (image src "" (str "image")))) - , ("cite", citation "cite" NormalCitation False) + , ("enquote", enquote) + , ("cite", citation "cite" AuthorInText 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) - , ("footcite", citation "footcite" NormalCitation False) + , ("footcite", inNote <$> citation "footcite" NormalCitation False) , ("parencite", citation "parencite" NormalCitation False) , ("supercite", citation "supercite" NormalCitation False) - , ("footcitetext", citation "footcitetext" NormalCitation False) + , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False) , ("citeyearpar", citation "citeyearpar" SuppressAuthor False) , ("citeyear", citation "citeyear" SuppressAuthor False) , ("autocite*", citation "autocite*" SuppressAuthor False) @@ -450,15 +457,15 @@ inlineCommands = M.fromList $ , ("textcites", citation "textcites" AuthorInText True) , ("cites", citation "cites" NormalCitation True) , ("autocites", citation "autocites" NormalCitation True) - , ("footcites", citation "footcites" NormalCitation True) + , ("footcites", inNote <$> citation "footcites" NormalCitation True) , ("parencites", citation "parencites" NormalCitation True) , ("supercites", citation "supercites" NormalCitation True) - , ("footcitetexts", citation "footcitetexts" NormalCitation True) + , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True) , ("Autocite", citation "Autocite" NormalCitation False) , ("Footcite", citation "Footcite" NormalCitation False) , ("Parencite", citation "Parencite" NormalCitation False) , ("Supercite", citation "Supercite" NormalCitation False) - , ("Footcitetext", citation "Footcitetext" NormalCitation False) + , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False) , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False) , ("Citeyear", citation "Citeyear" SuppressAuthor False) , ("Autocite*", citation "Autocite*" SuppressAuthor False) @@ -471,7 +478,7 @@ inlineCommands = M.fromList $ , ("Footcites", citation "Footcites" NormalCitation True) , ("Parencites", citation "Parencites" NormalCitation True) , ("Supercites", citation "Supercites" NormalCitation True) - , ("Footcitetexts", citation "Footcitetexts" NormalCitation True) + , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True) , ("citetext", complexNatbibCitation NormalCitation) , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *> complexNatbibCitation AuthorInText) @@ -479,7 +486,11 @@ inlineCommands = M.fromList $ ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: - [ "index", "nocite" ] + [ "noindent", "index", "nocite" ] + +inNote :: Inlines -> Inlines +inNote ils = + note $ para $ ils <> str "." unescapeURL :: String -> String unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs @@ -489,6 +500,14 @@ unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs unescapeURL (x:xs) = x:unescapeURL xs unescapeURL [] = "" +enquote :: LP Inlines +enquote = do + skipopts + context <- stateQuoteContext <$> getState + if context == InDoubleQuote + then singleQuoted <$> withQuoteContext InSingleQuote tok + else doubleQuoted <$> withQuoteContext InDoubleQuote tok + doverb :: LP Inlines doverb = do marker <- anyChar @@ -645,11 +664,7 @@ inlineText :: LP Inlines inlineText = str <$> many1 inlineChar inlineChar :: LP Char -inlineChar = satisfy $ \c -> - not (c == '\\' || c == '$' || c == '%' || c == '^' || c == '_' || - c == '&' || c == '~' || c == '#' || c == '{' || c == '}' || - c == '^' || c == '\'' || c == '`' || c == '-' || c == ']' || - c == ' ' || c == '\t' || c == '\n' ) +inlineChar = noneOf "\\$%^_&~#{}^'`-[] \t\n" environment :: LP Blocks environment = do @@ -662,7 +677,7 @@ environment = do rawEnv :: String -> LP Blocks rawEnv name = do let addBegin x = "\\begin{" ++ name ++ "}" ++ x - parseRaw <- stateParseRaw `fmap` getState + parseRaw <- getOption readerParseRaw if parseRaw then (rawBlock "latex" . addBegin) <$> (withRaw (env name blocks) >>= applyMacros' . snd) @@ -670,29 +685,52 @@ rawEnv name = do -- | Replace "include" commands with file contents. handleIncludes :: String -> IO String -handleIncludes [] = return [] -handleIncludes ('\\':xs) = +handleIncludes = handleIncludes' [] + +-- parents parameter prevents infinite include loops +handleIncludes' :: [FilePath] -> String -> IO String +handleIncludes' _ [] = return [] +handleIncludes' parents ('%':xs) = handleIncludes' parents + $ drop 1 $ dropWhile (/='\n') xs +handleIncludes' parents ('\\':xs) = case runParser include defaultParserState "input" ('\\':xs) of - Right (fs, rest) -> do let getfile f = E.catch (UTF8.readFile f) - (\(_::E.IOException) -> return "") - yss <- mapM getfile fs - (intercalate "\n" yss ++) `fmap` - handleIncludes rest + Right (fs, rest) -> do yss <- mapM (\f -> if f `elem` parents + then "" <$ warn ("Include file loop in '" + ++ f ++ "'.") + else readTeXFile f >>= + handleIncludes' (f:parents)) fs + rest' <- handleIncludes' parents rest + return $ intercalate "\n" yss ++ rest' _ -> case runParser (verbCmd <|> verbatimEnv) defaultParserState - "input" ('\\':xs) of - Right (r, rest) -> (r ++) `fmap` handleIncludes rest - _ -> ('\\':) `fmap` handleIncludes xs -handleIncludes (x:xs) = (x:) `fmap` handleIncludes xs + "input" ('\\':xs) of + Right (r, rest) -> (r ++) `fmap` handleIncludes' parents rest + _ -> ('\\':) `fmap` handleIncludes' parents xs +handleIncludes' parents (x:xs) = (x:) `fmap` handleIncludes' parents xs + +readTeXFile :: FilePath -> IO String +readTeXFile f = do + texinputs <- E.catch (getEnv "TEXINPUTS") $ \(_ :: E.SomeException) -> + return "." + let ds = splitBy (==':') texinputs + readFileFromDirs ds f + +readFileFromDirs :: [FilePath] -> FilePath -> IO String +readFileFromDirs [] _ = return "" +readFileFromDirs (d:ds) f = + E.catch (UTF8.readFile $ d </> f) $ \(_ :: E.SomeException) -> + readFileFromDirs ds f include :: LP ([FilePath], String) include = do - name <- controlSeq "include" <|> controlSeq "usepackage" + name <- controlSeq "include" + <|> controlSeq "input" + <|> controlSeq "usepackage" skipopts fs <- (splitBy (==',')) <$> braced rest <- getInput - let fs' = if name == "include" - then map (flip replaceExtension ".tex") fs - else map (flip replaceExtension ".sty") fs + let fs' = if name == "usepackage" + then map (flip replaceExtension ".sty") fs + else map (flip replaceExtension ".tex") fs return (fs', rest) verbCmd :: LP (String, String) @@ -715,15 +753,13 @@ verbatimEnv = do rest <- getInput return (r,rest) -rawLaTeXBlock :: GenParser Char ParserState String -rawLaTeXBlock = snd <$> withRaw (environment <|> blockCommand) +rawLaTeXBlock :: Parser [Char] ParserState String +rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand)) -rawLaTeXInline :: GenParser Char ParserState Inline +rawLaTeXInline :: Parser [Char] ParserState Inline rawLaTeXInline = do - (res, raw) <- withRaw inlineCommand - if res == mempty - then return (Str "") - else RawInline "latex" <$> (applyMacros' raw) + raw <- (snd <$> withRaw inlineCommand) <|> (snd <$> withRaw blockCommand) + RawInline "latex" <$> applyMacros' raw environments :: M.Map String (LP Blocks) environments = M.fromList @@ -737,7 +773,7 @@ environments = M.fromList , ("itemize", bulletList <$> listenv "itemize" (many item)) , ("description", definitionList <$> listenv "description" (many descItem)) , ("enumerate", ordered_list) - , ("code", failUnlessLHS *> + , ("code", guardEnabled Ext_literate_haskell *> (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> verbEnv "code")) , ("verbatim", codeBlock <$> (verbEnv "verbatim")) @@ -745,6 +781,9 @@ environments = M.fromList , ("lstlisting", codeBlock <$> (verbEnv "lstlisting")) , ("minted", liftA2 (\l c -> codeBlockWith ("",[l],[]) c) (grouped (many1 $ satisfy (/= '}'))) (verbEnv "minted")) + , ("obeylines", parseFromString + (para . trimInlines . mconcat <$> many inline) =<< + intercalate "\\\\\n" . lines <$> verbEnv "obeylines") , ("displaymath", mathEnv Nothing "displaymath") , ("equation", mathEnv Nothing "equation") , ("equation*", mathEnv Nothing "equation*") @@ -801,7 +840,9 @@ descItem = do return (ils, [bs]) env :: String -> LP a -> LP a -env name p = p <* (controlSeq "end" *> braced >>= guard . (== name)) +env name p = p <* + (try (controlSeq "end" *> braced >>= guard . (== name)) + <?> ("\\end{" ++ name ++ "}")) listenv :: String -> LP a -> LP a listenv name p = try $ do diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 51a727996..1f57d1918 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Markdown Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -28,31 +28,56 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of markdown-formatted plain text to 'Pandoc' document. -} -module Text.Pandoc.Readers.Markdown ( readMarkdown ) where +module Text.Pandoc.Readers.Markdown ( readMarkdown, + readMarkdownWithWarnings ) where -import Data.List ( transpose, sortBy, findIndex, intercalate ) +import Data.List ( transpose, sortBy, findIndex, intersperse, intercalate ) import qualified Data.Map as M import Data.Ord ( comparing ) -import Data.Char ( isAlphaNum ) +import Data.Char ( isAlphaNum, toLower ) import Data.Maybe import Text.Pandoc.Definition -import Text.Pandoc.Generic +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Builder (Inlines, Blocks, trimInlines, (<>)) +import Text.Pandoc.Options import Text.Pandoc.Shared -import Text.Pandoc.Parsing +import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXBlock ) import Text.Pandoc.Readers.HTML ( htmlTag, htmlInBalanced, isInlineTag, isBlockTag, isTextTag, isCommentTag ) import Text.Pandoc.XML ( fromEntities ) -import Text.ParserCombinators.Parsec -import Control.Monad (when, liftM, guard, mzero) +import Text.Pandoc.Biblio (processBiblio) +import qualified Text.CSL as CSL +import Data.Monoid (mconcat, mempty) +import Control.Applicative ((<$>), (<*), (*>), (<$)) +import Control.Monad import Text.HTML.TagSoup import Text.HTML.TagSoup.Match (tagOpen) +import qualified Data.Set as Set + +type MarkdownParser = Parser [Char] ParserState -- | Read markdown from an input string and return a Pandoc document. -readMarkdown :: ParserState -- ^ Parser state, including options for parser - -> String -- ^ String to parse (assuming @'\n'@ line endings) +readMarkdown :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) -> Pandoc -readMarkdown state s = (readWith parseMarkdown) state (s ++ "\n\n") +readMarkdown opts s = + (readWith parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + +-- | Read markdown from an input string and return a pair of a Pandoc document +-- and a list of warnings. +readMarkdownWithWarnings :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> (Pandoc, [String]) +readMarkdownWithWarnings opts s = + (readWith parseMarkdownWithWarnings) def{ stateOptions = opts } (s ++ "\n\n") + where parseMarkdownWithWarnings = do + doc <- parseMarkdown + warnings <- stateWarnings <$> getState + return (doc, warnings) + +trimInlinesF :: F Inlines -> F Inlines +trimInlinesF = liftM trimInlines -- -- Constants and data structure definitions @@ -70,7 +95,7 @@ isHruleChar '-' = True isHruleChar '_' = True isHruleChar _ = False -setextHChars :: [Char] +setextHChars :: String setextHChars = "=-" isBlank :: Char -> Bool @@ -83,71 +108,76 @@ isBlank _ = False -- auxiliary functions -- -indentSpaces :: GenParser Char ParserState [Char] +isNull :: F Inlines -> Bool +isNull ils = B.isNull $ runF ils def + +spnl :: Parser [Char] st () +spnl = try $ do + skipSpaces + optional newline + skipSpaces + notFollowedBy (char '\n') + +indentSpaces :: MarkdownParser String indentSpaces = try $ do - state <- getState - let tabStop = stateTabStop state + tabStop <- getOption readerTabStop count tabStop (char ' ') <|> string "\t" <?> "indentation" -nonindentSpaces :: GenParser Char ParserState [Char] +nonindentSpaces :: MarkdownParser String nonindentSpaces = do - state <- getState - let tabStop = stateTabStop state + tabStop <- getOption readerTabStop sps <- many (char ' ') - if length sps < tabStop + if length sps < tabStop then return sps else unexpected "indented line" -skipNonindentSpaces :: GenParser Char ParserState () +skipNonindentSpaces :: MarkdownParser () skipNonindentSpaces = do - state <- getState - atMostSpaces (stateTabStop state - 1) + tabStop <- getOption readerTabStop + atMostSpaces (tabStop - 1) -atMostSpaces :: Int -> GenParser Char ParserState () +atMostSpaces :: Int -> MarkdownParser () atMostSpaces 0 = notFollowedBy (char ' ') atMostSpaces n = (char ' ' >> atMostSpaces (n-1)) <|> return () -litChar :: GenParser Char ParserState Char +litChar :: MarkdownParser Char litChar = escapedChar' <|> noneOf "\n" - <|> (newline >> notFollowedBy blankline >> return ' ') - --- | Fail unless we're at beginning of a line. -failUnlessBeginningOfLine :: GenParser tok st () -failUnlessBeginningOfLine = do - pos <- getPosition - if sourceColumn pos == 1 then return () else fail "not beginning of line" + <|> try (newline >> notFollowedBy blankline >> return ' ') -- | Parse a sequence of inline elements between square brackets, -- including inlines between balanced pairs of square brackets. -inlinesInBalancedBrackets :: GenParser Char ParserState Inline - -> GenParser Char ParserState [Inline] -inlinesInBalancedBrackets parser = try $ do +inlinesInBalancedBrackets :: MarkdownParser (F Inlines) +inlinesInBalancedBrackets = charsInBalancedBrackets >>= + parseFromString (trimInlinesF . mconcat <$> many inline) + +charsInBalancedBrackets :: MarkdownParser [Char] +charsInBalancedBrackets = do char '[' - result <- manyTill ( (do lookAhead $ try $ do (Str res) <- parser - guard (res == "[") - bal <- inlinesInBalancedBrackets parser - return $ [Str "["] ++ bal ++ [Str "]"]) - <|> (count 1 parser)) - (char ']') + result <- manyTill ( many1 (noneOf "`[]\n") + <|> (snd <$> withRaw code) + <|> ((\xs -> '[' : xs ++ "]") <$> charsInBalancedBrackets) + <|> count 1 (satisfy (/='\n')) + <|> (newline >> notFollowedBy blankline >> return "\n") + ) (char ']') return $ concat result -- -- document structure -- -titleLine :: GenParser Char ParserState [Inline] +titleLine :: MarkdownParser (F Inlines) titleLine = try $ do char '%' skipSpaces res <- many $ (notFollowedBy newline >> inline) <|> try (endline >> whitespace) newline - return $ normalizeSpaces res + return $ trimInlinesF $ mconcat res -authorsLine :: GenParser Char ParserState [[Inline]] -authorsLine = try $ do +authorsLine :: MarkdownParser (F [Inlines]) +authorsLine = try $ do char '%' skipSpaces authors <- sepEndBy (many (notFollowedBy (satisfy $ \c -> @@ -155,67 +185,73 @@ authorsLine = try $ do (char ';' <|> try (newline >> notFollowedBy blankline >> spaceChar)) newline - return $ filter (not . null) $ map normalizeSpaces authors + return $ sequence $ filter (not . isNull) $ map (trimInlinesF . mconcat) authors -dateLine :: GenParser Char ParserState [Inline] +dateLine :: MarkdownParser (F Inlines) dateLine = try $ do char '%' skipSpaces - date <- manyTill inline newline - return $ normalizeSpaces date - -titleBlock :: GenParser Char ParserState ([Inline], [[Inline]], [Inline]) -titleBlock = try $ do - failIfStrict - title <- option [] titleLine - author <- option [] authorsLine - date <- option [] dateLine + trimInlinesF . mconcat <$> manyTill inline newline + +titleBlock :: MarkdownParser (F Inlines, F [Inlines], F Inlines) +titleBlock = pandocTitleBlock <|> mmdTitleBlock + +pandocTitleBlock :: MarkdownParser (F Inlines, F [Inlines], F Inlines) +pandocTitleBlock = try $ do + guardEnabled Ext_pandoc_title_block + title <- option mempty titleLine + author <- option (return []) authorsLine + date <- option mempty dateLine optional blanklines return (title, author, date) -parseMarkdown :: GenParser Char ParserState Pandoc +mmdTitleBlock :: MarkdownParser (F Inlines, F [Inlines], F Inlines) +mmdTitleBlock = try $ do + guardEnabled Ext_mmd_title_block + kvPairs <- many1 kvPair + blanklines + let title = maybe mempty return $ lookup "title" kvPairs + let author = maybe mempty (\x -> return [x]) $ lookup "author" kvPairs + let date = maybe mempty return $ lookup "date" kvPairs + return (title, author, date) + +kvPair :: MarkdownParser (String, Inlines) +kvPair = try $ do + key <- many1Till (alphaNum <|> oneOf "_- ") (char ':') + val <- manyTill anyChar + (try $ newline >> lookAhead (blankline <|> nonspaceChar)) + let key' = concat $ words $ map toLower key + let val' = trimInlines $ B.text val + return (key',val') + +parseMarkdown :: MarkdownParser Pandoc parseMarkdown = do -- markdown allows raw HTML - updateState (\state -> state { stateParseRaw = True }) - 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... - st <- getState - let firstPassParser = referenceKey - <|> (if stateStrict st then pzero else noteBlock) - <|> liftM snd (withRaw codeBlockDelimited) - <|> lineClump - docMinusKeys <- liftM concat $ manyTill firstPassParser eof - setInput docMinusKeys - setPosition startPos - st' <- getState - let reversedNotes = stateNotes st' - updateState $ \s -> s { stateNotes = reverse reversedNotes } - -- now parse it for real... - (title, author, date) <- option ([],[],[]) titleBlock + updateState $ \state -> state { stateOptions = + let oldOpts = stateOptions state in + oldOpts{ readerParseRaw = True } } + (title, authors, date) <- option (mempty,return [],mempty) titleBlock blocks <- parseBlocks - let doc = Pandoc (Meta title author date) $ filter (/= Null) blocks - -- if there are labeled examples, change references into numbers - examples <- liftM stateExamples getState - let handleExampleRef :: Inline -> Inline - handleExampleRef z@(Str ('@':xs)) = - case M.lookup xs examples of - Just n -> Str (show n) - Nothing -> z - handleExampleRef z = z - if M.null examples - then return doc - else return $ bottomUp handleExampleRef doc - --- --- initial pass for references and notes --- - -referenceKey :: GenParser Char ParserState [Char] + st <- getState + mbsty <- getOption readerCitationStyle + refs <- getOption readerReferences + return $ processBiblio mbsty refs + $ B.setTitle (runF title st) + $ B.setAuthors (runF authors st) + $ B.setDate (runF date st) + $ B.doc $ runF blocks st + +addWarning :: Maybe SourcePos -> String -> MarkdownParser () +addWarning mbpos msg = + updateState $ \st -> st{ + stateWarnings = (msg ++ maybe "" (\pos -> " " ++ show pos) mbpos) : + stateWarnings st } + +referenceKey :: MarkdownParser (F Blocks) referenceKey = try $ do - startPos <- getPosition + pos <- getPosition skipNonindentSpaces - lab <- reference + (_,raw) <- reference char ':' skipSpaces >> optional newline >> skipSpaces >> notFollowedBy (char '[') let sourceURL = liftM unwords $ many $ try $ do @@ -223,139 +259,197 @@ referenceKey = try $ do skipMany spaceChar optional $ newline >> notFollowedBy blankline skipMany spaceChar - notFollowedBy' reference + notFollowedBy' (() <$ reference) many1 $ escapedChar' <|> satisfy (not . isBlank) let betweenAngles = try $ char '<' >> manyTill (escapedChar' <|> litChar) (char '>') src <- try betweenAngles <|> sourceURL tit <- option "" referenceTitle + -- currently we just ignore MMD-style link/image attributes + _kvs <- option [] $ guardEnabled Ext_link_attributes + >> many (spnl >> keyValAttr) blanklines - endPos <- getPosition - let target = (escapeURI $ removeTrailingSpace src, tit) + let target = (escapeURI $ trimr src, tit) st <- getState let oldkeys = stateKeys st - updateState $ \s -> s { stateKeys = M.insert (toKey lab) target oldkeys } - -- return blanks so line count isn't affected - return $ replicate (sourceLine endPos - sourceLine startPos) '\n' - -referenceTitle :: GenParser Char ParserState String + let key = toKey raw + case M.lookup key oldkeys of + Just _ -> addWarning (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" + Nothing -> return () + updateState $ \s -> s { stateKeys = M.insert key target oldkeys } + return $ return mempty + +referenceTitle :: MarkdownParser String referenceTitle = try $ do skipSpaces >> optional newline >> skipSpaces - tit <- (charsInBalanced '(' ')' litChar >>= return . unwords . words) - <|> do delim <- char '\'' <|> char '"' - manyTill litChar (try (char delim >> skipSpaces >> - notFollowedBy (noneOf ")\n"))) - return $ fromEntities tit + let parenTit = charsInBalanced '(' ')' litChar + fromEntities <$> (quotedTitle '"' <|> quotedTitle '\'' <|> parenTit) -noteMarker :: GenParser Char ParserState [Char] +-- A link title in quotes +quotedTitle :: Char -> MarkdownParser String +quotedTitle c = try $ do + char c + notFollowedBy spaces + let pEnder = try $ char c >> notFollowedBy (satisfy isAlphaNum) + let regChunk = many1 (noneOf ['\\','\n',c]) <|> count 1 litChar + let nestedChunk = (\x -> [c] ++ x ++ [c]) <$> quotedTitle c + unwords . words . 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 :: MarkdownParser (F Blocks) +abbrevKey = do + guardEnabled Ext_abbreviations + try $ do + char '*' + reference + char ':' + skipMany (satisfy (/= '\n')) + blanklines + return $ return mempty + +noteMarker :: MarkdownParser String noteMarker = string "[^" >> many1Till (satisfy $ not . isBlank) (char ']') -rawLine :: GenParser Char ParserState [Char] +rawLine :: MarkdownParser String rawLine = try $ do notFollowedBy blankline notFollowedBy' $ try $ skipNonindentSpaces >> noteMarker optional indentSpaces anyLine -rawLines :: GenParser Char ParserState [Char] +rawLines :: MarkdownParser String rawLines = do first <- anyLine rest <- many rawLine return $ unlines (first:rest) -noteBlock :: GenParser Char ParserState [Char] +noteBlock :: MarkdownParser (F Blocks) noteBlock = try $ do - startPos <- getPosition + pos <- getPosition skipNonindentSpaces ref <- noteMarker char ':' optional blankline optional indentSpaces - raw <- sepBy rawLines - (try (blankline >> indentSpaces >> - notFollowedBy blankline)) + first <- rawLines + rest <- many $ try $ blanklines >> indentSpaces >> rawLines + let raw = unlines (first:rest) ++ "\n" optional blanklines - endPos <- getPosition - let newnote = (ref, (intercalate "\n" raw) ++ "\n\n") - st <- getState - let oldnotes = stateNotes st - updateState $ \s -> s { stateNotes = newnote : oldnotes } - -- return blanks so line count isn't affected - return $ replicate (sourceLine endPos - sourceLine startPos) '\n' + parsed <- parseFromString parseBlocks raw + let newnote = (ref, parsed) + oldnotes <- stateNotes' <$> getState + case lookup ref oldnotes of + Just _ -> addWarning (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" + Nothing -> return () + updateState $ \s -> s { stateNotes' = newnote : oldnotes } + return mempty -- -- parsing blocks -- -parseBlocks :: GenParser Char ParserState [Block] -parseBlocks = manyTill block eof - -block :: GenParser Char ParserState Block -block = do - st <- getState - choice (if stateStrict st - then [ header - , codeBlockIndented - , blockQuote - , hrule - , bulletList - , orderedList - , htmlBlock - , para - , plain - , nullBlock ] - else [ codeBlockDelimited - , macro - , header - , table - , codeBlockIndented - , lhsCodeBlock - , blockQuote - , hrule - , bulletList - , orderedList - , definitionList - , rawTeXBlock - , para - , rawHtmlBlocks - , plain - , nullBlock ]) <?> "block" +parseBlocks :: MarkdownParser (F Blocks) +parseBlocks = mconcat <$> manyTill block eof + +block :: MarkdownParser (F Blocks) +block = choice [ codeBlockFenced + , codeBlockBackticks + , guardEnabled Ext_latex_macros *> (mempty <$ macro) + , header + , rawTeXBlock + , htmlBlock + , lineBlock + , table + , codeBlockIndented + , lhsCodeBlock + , blockQuote + , hrule + , bulletList + , orderedList + , definitionList + , noteBlock + , referenceKey + , abbrevKey + , para + , plain + ] <?> "block" -- -- header blocks -- -header :: GenParser Char ParserState Block +header :: MarkdownParser (F Blocks) header = setextHeader <|> atxHeader <?> "header" -atxHeader :: GenParser Char ParserState Block +-- returns unique identifier +addToHeaderList :: Attr -> F Inlines -> MarkdownParser Attr +addToHeaderList (ident,classes,kvs) text = do + let headerList = B.toList $ runF text defaultParserState + updateState $ \st -> st{ stateHeaders = headerList : stateHeaders st } + (do guardEnabled Ext_auto_identifiers + ids <- stateIdentifiers `fmap` getState + let id' = if null ident + then uniqueIdent headerList ids + else ident + updateState $ \st -> st{ stateIdentifiers = id' : ids } + return (id',classes,kvs)) <|> return ("",classes,kvs) + +atxHeader :: MarkdownParser (F Blocks) atxHeader = try $ do level <- many1 (char '#') >>= return . length notFollowedBy (char '.' <|> char ')') -- this would be a list skipSpaces - text <- manyTill inline atxClosing >>= return . normalizeSpaces - return $ Header level text + text <- trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline) + attr <- atxClosing + attr' <- addToHeaderList attr text + return $ B.headerWith attr' level <$> text + +atxClosing :: MarkdownParser Attr +atxClosing = try $ do + attr' <- option nullAttr + (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) + skipMany (char '#') + skipSpaces + attr <- option attr' + (guardEnabled Ext_header_attributes >> attributes) + blanklines + return attr + +setextHeaderEnd :: MarkdownParser Attr +setextHeaderEnd = try $ do + attr <- option nullAttr + $ (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) + <|> (guardEnabled Ext_header_attributes >> attributes) + blanklines + return attr -atxClosing :: GenParser Char st [Char] -atxClosing = try $ skipMany (char '#') >> blanklines +mmdHeaderIdentifier :: MarkdownParser Attr +mmdHeaderIdentifier = do + ident <- stripFirstAndLast . snd <$> reference + skipSpaces + return (ident,[],[]) -setextHeader :: GenParser Char ParserState Block +setextHeader :: MarkdownParser (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 - text <- many1Till inline newline + text <- trimInlinesF . mconcat <$> many1 (notFollowedBy setextHeaderEnd >> inline) + attr <- setextHeaderEnd underlineChar <- oneOf setextHChars many (char underlineChar) blanklines let level = (fromMaybe 0 $ findIndex (== underlineChar) setextHChars) + 1 - return $ Header level (normalizeSpaces text) + attr' <- addToHeaderList attr text + return $ B.headerWith attr' level <$> text -- -- hrule block -- -hrule :: GenParser Char st Block +hrule :: Parser [Char] st (F Blocks) hrule = try $ do skipSpaces start <- satisfy isHruleChar @@ -363,32 +457,26 @@ hrule = try $ do skipMany (spaceChar <|> char start) newline optional blanklines - return HorizontalRule + return $ return B.horizontalRule -- -- code blocks -- -indentedLine :: GenParser Char ParserState [Char] +indentedLine :: MarkdownParser String indentedLine = indentSpaces >> manyTill anyChar newline >>= return . (++ "\n") blockDelimiter :: (Char -> Bool) -> Maybe Int - -> GenParser Char st (Int, (String, [String], [(String, String)]), Char) + -> Parser [Char] st Int blockDelimiter f len = try $ do c <- lookAhead (satisfy f) - size <- case len of - Just l -> count l (char c) >> many (char c) >> return l - Nothing -> count 3 (char c) >> many (char c) >>= - return . (+ 3) . length - many spaceChar - attr <- option ([],[],[]) - $ attributes -- ~~~ {.ruby} - <|> (many1 alphaNum >>= \x -> return ([],[x],[])) -- github variant ```ruby - blankline - return (size, attr, c) + case len of + Just l -> count l (char c) >> many (char c) >> return l + Nothing -> count 3 (char c) >> many (char c) >>= + return . (+ 3) . length -attributes :: GenParser Char st ([Char], [[Char]], [([Char], [Char])]) +attributes :: Parser [Char] st (String, [String], [(String, String)]) attributes = try $ do char '{' spnl @@ -400,28 +488,28 @@ attributes = try $ do | otherwise = firstNonNull xs return (firstNonNull $ reverse ids, concat classes, concat keyvals) -attribute :: GenParser Char st ([Char], [[Char]], [([Char], [Char])]) +attribute :: Parser [Char] st (String, [String], [(String, String)]) attribute = identifierAttr <|> classAttr <|> keyValAttr -identifier :: GenParser Char st [Char] +identifier :: Parser [Char] st String identifier = do first <- letter rest <- many $ alphaNum <|> oneOf "-_:." return (first:rest) -identifierAttr :: GenParser Char st ([Char], [a], [a1]) +identifierAttr :: Parser [Char] st (String, [a], [a1]) identifierAttr = try $ do char '#' result <- identifier return (result,[],[]) -classAttr :: GenParser Char st ([Char], [[Char]], [a]) +classAttr :: Parser [Char] st (String, [String], [a]) classAttr = try $ do char '.' result <- identifier return ("",[result],[]) -keyValAttr :: GenParser Char st ([Char], [a], [([Char], [Char])]) +keyValAttr :: Parser [Char] st (String, [a], [(String, String)]) keyValAttr = try $ do key <- identifier char '=' @@ -430,33 +518,49 @@ keyValAttr = try $ do <|> many nonspaceChar return ("",[],[(key,val)]) -codeBlockDelimited :: GenParser Char st Block -codeBlockDelimited = try $ do - (size, attr, c) <- blockDelimiter (\c -> c == '~' || c == '`') Nothing - contents <- manyTill anyLine (blockDelimiter (== c) (Just size)) +codeBlockFenced :: MarkdownParser (F Blocks) +codeBlockFenced = try $ do + guardEnabled Ext_fenced_code_blocks + size <- blockDelimiter (=='~') Nothing + skipMany spaceChar + attr <- option ([],[],[]) $ + guardEnabled Ext_fenced_code_attributes >> attributes + blankline + contents <- manyTill anyLine (blockDelimiter (=='~') (Just size)) blanklines - return $ CodeBlock attr $ intercalate "\n" contents + return $ return $ B.codeBlockWith attr $ intercalate "\n" contents + +codeBlockBackticks :: MarkdownParser (F Blocks) +codeBlockBackticks = try $ do + guardEnabled Ext_backtick_code_blocks + blockDelimiter (=='`') (Just 3) + skipMany spaceChar + cls <- many1 alphaNum + blankline + contents <- manyTill anyLine $ blockDelimiter (=='`') (Just 3) + blanklines + return $ return $ B.codeBlockWith ("",[cls],[]) $ intercalate "\n" contents -codeBlockIndented :: GenParser Char ParserState Block +codeBlockIndented :: MarkdownParser (F Blocks) codeBlockIndented = do - contents <- many1 (indentedLine <|> + contents <- many1 (indentedLine <|> try (do b <- blanklines l <- indentedLine return $ b ++ l)) optional blanklines - st <- getState - return $ CodeBlock ("", stateIndentedCodeClasses st, []) $ + classes <- getOption readerIndentedCodeClasses + return $ return $ B.codeBlockWith ("", classes, []) $ stripTrailingNewlines $ concat contents -lhsCodeBlock :: GenParser Char ParserState Block +lhsCodeBlock :: MarkdownParser (F Blocks) lhsCodeBlock = do - failUnlessLHS - liftM (CodeBlock ("",["sourceCode","literate","haskell"],[])) - (lhsCodeBlockBird <|> lhsCodeBlockLaTeX) - <|> liftM (CodeBlock ("",["sourceCode","haskell"],[])) - lhsCodeBlockInverseBird + guardEnabled Ext_literate_haskell + (return . B.codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> + (lhsCodeBlockBird <|> lhsCodeBlockLaTeX)) + <|> (return . B.codeBlockWith ("",["sourceCode","haskell"],[]) <$> + lhsCodeBlockInverseBird) -lhsCodeBlockLaTeX :: GenParser Char ParserState String +lhsCodeBlockLaTeX :: MarkdownParser String lhsCodeBlockLaTeX = try $ do string "\\begin{code}" manyTill spaceChar newline @@ -464,13 +568,13 @@ lhsCodeBlockLaTeX = try $ do blanklines return $ stripTrailingNewlines contents -lhsCodeBlockBird :: GenParser Char ParserState String +lhsCodeBlockBird :: MarkdownParser String lhsCodeBlockBird = lhsCodeBlockBirdWith '>' -lhsCodeBlockInverseBird :: GenParser Char ParserState String +lhsCodeBlockInverseBird :: MarkdownParser String lhsCodeBlockInverseBird = lhsCodeBlockBirdWith '<' -lhsCodeBlockBirdWith :: Char -> GenParser Char ParserState String +lhsCodeBlockBirdWith :: Char -> MarkdownParser String lhsCodeBlockBirdWith c = try $ do pos <- getPosition when (sourceColumn pos /= 1) $ fail "Not in first column" @@ -482,77 +586,78 @@ lhsCodeBlockBirdWith c = try $ do blanklines return $ intercalate "\n" lns' -birdTrackLine :: Char -> GenParser Char st [Char] +birdTrackLine :: Char -> Parser [Char] st String birdTrackLine c = try $ do char c -- allow html tags on left margin: when (c == '<') $ notFollowedBy letter manyTill anyChar newline - -- -- block quotes -- -emailBlockQuoteStart :: GenParser Char ParserState Char +emailBlockQuoteStart :: MarkdownParser Char emailBlockQuoteStart = try $ skipNonindentSpaces >> char '>' >>~ optional (char ' ') -emailBlockQuote :: GenParser Char ParserState [[Char]] +emailBlockQuote :: MarkdownParser [String] emailBlockQuote = try $ do emailBlockQuoteStart - raw <- sepBy (many (nonEndline <|> - (try (endline >> notFollowedBy emailBlockQuoteStart >> - return '\n')))) - (try (newline >> emailBlockQuoteStart)) + let emailLine = many $ 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 :: GenParser Char ParserState Block -blockQuote = do +blockQuote :: MarkdownParser (F Blocks) +blockQuote = do raw <- emailBlockQuote -- parse the extracted block, which may contain various block elements: contents <- parseFromString parseBlocks $ (intercalate "\n" raw) ++ "\n\n" - return $ BlockQuote contents - + return $ B.blockQuote <$> contents + -- -- list blocks -- -bulletListStart :: GenParser Char ParserState () +bulletListStart :: MarkdownParser () 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 + notFollowedBy' (() <$ hrule) -- because hrules start out just like lists satisfy isBulletListMarker spaceChar skipSpaces -anyOrderedListStart :: GenParser Char ParserState (Int, ListNumberStyle, ListNumberDelim) +anyOrderedListStart :: MarkdownParser (Int, ListNumberStyle, ListNumberDelim) anyOrderedListStart = try $ do optional newline -- if preceded by a Plain block in a list context skipNonindentSpaces notFollowedBy $ string "p." >> spaceChar >> digit -- page number - state <- getState - if stateStrict state - then do many1 digit - char '.' - spaceChar - return (1, DefaultStyle, DefaultDelim) - else do (num, style, delim) <- anyOrderedListMarker - -- if it could be an abbreviated first name, insist on more than one space - if delim == Period && (style == UpperAlpha || (style == UpperRoman && - num `elem` [1, 5, 10, 50, 100, 500, 1000])) - then char '\t' <|> (try $ char ' ' >> spaceChar) - else spaceChar - skipSpaces - return (num, style, delim) - -listStart :: GenParser Char ParserState () + (guardDisabled Ext_fancy_lists >> + do many1 digit + char '.' + spaceChar + return (1, DefaultStyle, DefaultDelim)) + <|> do (num, style, delim) <- anyOrderedListMarker + -- if it could be an abbreviated first name, insist on more than one space + if delim == Period && (style == UpperAlpha || (style == UpperRoman && + num `elem` [1, 5, 10, 50, 100, 500, 1000])) + then char '\t' <|> (try $ char ' ' >> spaceChar) + else spaceChar + skipSpaces + return (num, style, delim) + +listStart :: MarkdownParser () listStart = bulletListStart <|> (anyOrderedListStart >> return ()) -- parse a line of a list item (start = parser for beginning of list item) -listLine :: GenParser Char ParserState [Char] +listLine :: MarkdownParser String listLine = try $ do notFollowedBy blankline notFollowedBy' (do indentSpaces @@ -562,8 +667,8 @@ listLine = try $ do return $ concat chunks ++ "\n" -- parse raw text for one list item, excluding start marker and continuations -rawListItem :: GenParser Char ParserState a - -> GenParser Char ParserState [Char] +rawListItem :: MarkdownParser a + -> MarkdownParser String rawListItem start = try $ do start first <- listLine @@ -571,17 +676,17 @@ rawListItem start = try $ do blanks <- many blankline return $ concat (first:rest) ++ blanks --- continuation of a list item - indented and separated by blankline +-- continuation of a list item - indented and separated by blankline -- or (in compact lists) endline. -- note: nested lists are parsed as continuations -listContinuation :: GenParser Char ParserState [Char] +listContinuation :: MarkdownParser String listContinuation = try $ do lookAhead indentSpaces result <- many1 listContinuationLine blanks <- many blankline return $ concat result ++ blanks -listContinuationLine :: GenParser Char ParserState [Char] +listContinuationLine :: MarkdownParser String listContinuationLine = try $ do notFollowedBy blankline notFollowedBy' listStart @@ -589,8 +694,8 @@ listContinuationLine = try $ do result <- manyTill anyChar newline return $ result ++ "\n" -listItem :: GenParser Char ParserState a - -> GenParser Char ParserState [Block] +listItem :: MarkdownParser a + -> MarkdownParser (F Blocks) listItem start = try $ do first <- rawListItem start continuations <- many listContinuation @@ -606,38 +711,45 @@ listItem start = try $ do updateState (\st -> st {stateParserContext = oldContext}) return contents -orderedList :: GenParser Char ParserState Block +orderedList :: MarkdownParser (F Blocks) orderedList = try $ do (start, style, delim) <- lookAhead anyOrderedListStart - items <- many1 $ listItem $ try $ - do optional newline -- if preceded by a Plain block in a list context - skipNonindentSpaces - orderedListMarker style delim - return $ OrderedList (start, style, delim) $ compactify items - -bulletList :: GenParser Char ParserState Block -bulletList = - many1 (listItem bulletListStart) >>= return . BulletList . compactify + unless ((style == DefaultStyle || style == Decimal || style == Example) && + (delim == DefaultDelim || delim == Period)) $ + guardEnabled Ext_fancy_lists + when (style == Example) $ guardEnabled Ext_example_lists + items <- fmap sequence $ many1 $ listItem + ( try $ do + optional newline -- if preceded by Plain block in a list + skipNonindentSpaces + orderedListMarker style delim ) + start' <- option 1 $ guardEnabled Ext_startnum >> return start + return $ B.orderedListWith (start', style, delim) <$> fmap compactify' items + +bulletList :: MarkdownParser (F Blocks) +bulletList = do + items <- fmap sequence $ many1 $ listItem bulletListStart + return $ B.bulletList <$> fmap compactify' items -- definition lists -defListMarker :: GenParser Char ParserState () +defListMarker :: MarkdownParser () defListMarker = do sps <- nonindentSpaces char ':' <|> char '~' - st <- getState - let tabStop = stateTabStop st + tabStop <- getOption readerTabStop let remaining = tabStop - (length sps + 1) if remaining > 0 then count remaining (char ' ') <|> string "\t" - else pzero + else mzero return () -definitionListItem :: GenParser Char ParserState ([Inline], [[Block]]) +definitionListItem :: MarkdownParser (F (Inlines, [Blocks])) definitionListItem = try $ do + guardEnabled Ext_definition_lists -- first, see if this has any chance of being a definition list: lookAhead (anyLine >> optional blankline >> defListMarker) - term <- manyTill inline newline + term <- trimInlinesF . mconcat <$> manyTill inline newline optional blankline raw <- many1 defRawBlock state <- getState @@ -645,9 +757,9 @@ definitionListItem = try $ do -- parse the extracted block, which may contain various block elements: contents <- mapM (parseFromString parseBlocks) raw updateState (\st -> st {stateParserContext = oldContext}) - return ((normalizeSpaces term), contents) + return $ liftM2 (,) term (sequence contents) -defRawBlock :: GenParser Char ParserState [Char] +defRawBlock :: MarkdownParser String defRawBlock = try $ do defListMarker firstline <- anyLine @@ -659,119 +771,160 @@ defRawBlock = try $ do return $ unlines lns ++ trl return $ firstline ++ "\n" ++ unlines rawlines ++ trailing ++ cont -definitionList :: GenParser Char ParserState Block +definitionList :: MarkdownParser (F Blocks) definitionList = do - items <- many1 definitionListItem - -- "compactify" the definition list: - let defs = map snd items - let defBlocks = reverse $ concat $ concat defs - let isPara (Para _) = True + items <- fmap sequence $ many1 definitionListItem + return $ B.definitionList <$> fmap compactify'DL items + +compactify'DL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])] +compactify'DL items = + let defs = concatMap snd items + defBlocks = reverse $ concatMap B.toList defs + isPara (Para _) = True isPara _ = False - let items' = case take 1 defBlocks of - [Para x] -> if not $ any isPara (drop 1 defBlocks) - then let (t,ds) = last items - lastDef = last ds - ds' = init ds ++ - [init lastDef ++ [Plain x]] - in init items ++ [(t, ds')] - else items - _ -> items - return $ DefinitionList items' + in case defBlocks of + (Para x:_) -> if not $ any isPara (drop 1 defBlocks) + then let (t,ds) = last items + lastDef = B.toList $ last ds + ds' = init ds ++ + [B.fromList $ init lastDef ++ [Plain x]] + in init items ++ [(t, ds')] + else items + _ -> items -- -- paragraph block -- -isHtmlOrBlank :: Inline -> Bool -isHtmlOrBlank (RawInline "html" _) = True -isHtmlOrBlank (Space) = True -isHtmlOrBlank (LineBreak) = True -isHtmlOrBlank _ = False - -para :: GenParser Char ParserState Block -para = try $ do - result <- liftM normalizeSpaces $ many1 inline - guard $ not . all isHtmlOrBlank $ result - option (Plain result) $ try $ do - newline - blanklines <|> - (getState >>= guard . stateStrict >> - lookAhead (blockQuote <|> header) >> return "") - return $ Para result - -plain :: GenParser Char ParserState Block -plain = many1 inline >>~ spaces >>= return . Plain . normalizeSpaces - --- +para :: MarkdownParser (F Blocks) +para = try $ do + exts <- getOption readerExtensions + result <- trimInlinesF . mconcat <$> many1 inline + option (B.plain <$> result) + $ try $ do + newline + (blanklines >> return mempty) + <|> (guardDisabled Ext_blank_before_blockquote >> lookAhead blockQuote) + <|> (guardDisabled Ext_blank_before_header >> lookAhead header) + return $ do + result' <- result + case B.toList result' of + [Image alt (src,tit)] + | Ext_implicit_figures `Set.member` exts -> + -- the fig: at beginning of title indicates a figure + return $ B.para $ B.singleton + $ Image alt (src,'f':'i':'g':':':tit) + _ -> return $ B.para result' + +plain :: MarkdownParser (F Blocks) +plain = fmap B.plain . trimInlinesF . mconcat <$> many1 inline <* spaces + +-- -- raw html -- -htmlElement :: GenParser Char ParserState [Char] +htmlElement :: MarkdownParser String htmlElement = strictHtmlBlock <|> liftM snd (htmlTag isBlockTag) -htmlBlock :: GenParser Char ParserState Block -htmlBlock = try $ do - failUnlessBeginningOfLine +htmlBlock :: MarkdownParser (F Blocks) +htmlBlock = do + guardEnabled Ext_raw_html + res <- (guardEnabled Ext_markdown_in_html_blocks >> rawHtmlBlocks) + <|> htmlBlock' + return $ return $ B.rawBlock "html" res + +htmlBlock' :: MarkdownParser String +htmlBlock' = try $ do first <- htmlElement finalSpace <- many spaceChar finalNewlines <- many newline - return $ RawBlock "html" $ first ++ finalSpace ++ finalNewlines + return $ first ++ finalSpace ++ finalNewlines -strictHtmlBlock :: GenParser Char ParserState [Char] -strictHtmlBlock = do - failUnlessBeginningOfLine - htmlInBalanced (not . isInlineTag) +strictHtmlBlock :: MarkdownParser String +strictHtmlBlock = htmlInBalanced (not . isInlineTag) -rawVerbatimBlock :: GenParser Char ParserState String +rawVerbatimBlock :: MarkdownParser String rawVerbatimBlock = try $ do (TagOpen tag _, open) <- htmlTag (tagOpen (\t -> - t == "pre" || t == "style" || t == "script") - (const True)) + t == "pre" || t == "style" || t == "script") + (const True)) contents <- manyTill anyChar (htmlTag (~== TagClose tag)) return $ open ++ contents ++ renderTags [TagClose tag] -rawTeXBlock :: GenParser Char ParserState Block +rawTeXBlock :: MarkdownParser (F Blocks) rawTeXBlock = do - failIfStrict - result <- liftM (RawBlock "latex") rawLaTeXBlock - <|> liftM (RawBlock "context") rawConTeXtEnvironment + guardEnabled Ext_raw_tex + result <- (B.rawBlock "latex" <$> rawLaTeXBlock) + <|> (B.rawBlock "context" <$> rawConTeXtEnvironment) spaces - return result + return $ return result -rawHtmlBlocks :: GenParser Char ParserState Block +rawHtmlBlocks :: MarkdownParser String rawHtmlBlocks = do - htmlBlocks <- many1 $ do blk <- rawVerbatimBlock <|> - liftM snd (htmlTag isBlockTag) - sps <- do sp1 <- many spaceChar - sp2 <- option "" (blankline >> return "\n") - sp3 <- many spaceChar - sp4 <- option "" blanklines - return $ sp1 ++ sp2 ++ sp3 ++ sp4 - -- note: we want raw html to be able to - -- precede a code block, when separated - -- by a blank line - return $ blk ++ sps + htmlBlocks <- many1 $ try $ do + s <- rawVerbatimBlock <|> try ( + do (t,raw) <- htmlTag isBlockTag + exts <- getOption readerExtensions + -- if open tag, need markdown="1" if + -- markdown_attributes extension is set + case t of + TagOpen _ as + | Ext_markdown_attribute `Set.member` + exts -> + if "markdown" `notElem` + map fst as + then mzero + else return $ + stripMarkdownAttribute raw + | otherwise -> return raw + _ -> return raw ) + sps <- do sp1 <- many spaceChar + sp2 <- option "" (blankline >> return "\n") + sp3 <- many spaceChar + sp4 <- option "" blanklines + return $ sp1 ++ sp2 ++ sp3 ++ sp4 + -- note: we want raw html to be able to + -- precede a code block, when separated + -- by a blank line + return $ s ++ sps let combined = concat htmlBlocks - let combined' = if last combined == '\n' then init combined else combined - return $ RawBlock "html" combined' + return $ if last combined == '\n' then init combined else combined + +-- remove markdown="1" attribute +stripMarkdownAttribute :: String -> String +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 :: MarkdownParser (F Blocks) +lineBlock = try $ do + guardEnabled Ext_line_blocks + lines' <- lineBlockLines >>= + mapM (parseFromString (trimInlinesF . mconcat <$> many inline)) + return $ B.para <$> (mconcat $ intersperse (return B.linebreak) lines') -- -- Tables --- +-- -- Parse a dashed line with optional trailing spaces; return its length -- and the length including trailing space. -dashedLine :: Char - -> GenParser Char st (Int, Int) +dashedLine :: Char + -> Parser [Char] st (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many spaceChar return $ (length dashes, length $ dashes ++ sp) --- Parse a table header with dashed lines of '-' preceded by +-- Parse a table header with dashed lines of '-' preceded by -- one (or zero) line of text. -simpleTableHeader :: Bool -- ^ Headerless table - -> GenParser Char ParserState ([[Block]], [Alignment], [Int]) +simpleTableHeader :: Bool -- ^ Headerless table + -> MarkdownParser (F [Blocks], [Alignment], [Int]) simpleTableHeader headless = try $ do rawContent <- if headless then return "" @@ -784,84 +937,104 @@ simpleTableHeader headless = try $ do -- If no header, calculate alignment on basis of first row of text rawHeads <- liftM (tail . splitStringByIndices (init indices)) $ if headless - then lookAhead anyLine + then lookAhead anyLine else return rawContent let aligns = zipWith alignType (map (\a -> [a]) rawHeads) lengths let rawHeads' = if headless then replicate (length dashes) "" - else rawHeads - heads <- mapM (parseFromString (many plain)) $ - map removeLeadingTrailingSpace rawHeads' + else rawHeads + heads <- fmap sequence + $ mapM (parseFromString (mconcat <$> many plain)) + $ map trim rawHeads' return (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 :: [String] + -> Int + -> Alignment +alignType [] _ = AlignDefault +alignType strLst len = + let nonempties = filter (not . null) $ map trimr strLst + (leftSpace, rightSpace) = + case sortBy (comparing length) nonempties of + (x:_) -> (head x `elem` " \t", length 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 :: GenParser Char ParserState [Char] +tableFooter :: MarkdownParser String tableFooter = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> blanklines -- Parse a table separator - dashed line. -tableSep :: GenParser Char ParserState Char +tableSep :: MarkdownParser Char tableSep = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> char '\n' -- Parse a raw line and split it into chunks by indices. rawTableLine :: [Int] - -> GenParser Char ParserState [String] + -> MarkdownParser [String] rawTableLine indices = do notFollowedBy' (blanklines <|> tableFooter) line <- many1Till anyChar newline - return $ map removeLeadingTrailingSpace $ tail $ + return $ map trim $ tail $ splitStringByIndices (init indices) line -- Parse a table line and return a list of lists of blocks (columns). tableLine :: [Int] - -> GenParser Char ParserState [[Block]] -tableLine indices = rawTableLine indices >>= mapM (parseFromString (many plain)) + -> MarkdownParser (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 :: [Int] - -> GenParser Char ParserState [[Block]] + -> MarkdownParser (F [Blocks]) multilineRow indices = do colLines <- many1 (rawTableLine indices) let cols = map unlines $ transpose colLines - mapM (parseFromString (many plain)) cols + fmap sequence $ mapM (parseFromString (mconcat <$> many plain)) cols -- Parses a table caption: inlines beginning with 'Table:' -- and followed by blank lines. -tableCaption :: GenParser Char ParserState [Inline] +tableCaption :: MarkdownParser (F Inlines) tableCaption = try $ do + guardEnabled Ext_table_captions skipNonindentSpaces string ":" <|> string "Table:" - result <- many1 inline - blanklines - return $ normalizeSpaces result + trimInlinesF . mconcat <$> many1 inline <* blanklines -- Parse a simple table with '---' header and one line per row. simpleTable :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block + -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) simpleTable headless = do - Table c a _w h l <- tableWith (simpleTableHeader headless) tableLine + (aligns, _widths, heads', lines') <- + tableWith (simpleTableHeader headless) tableLine (return ()) (if headless then tableFooter else tableFooter <|> blanklines) - tableCaption -- Simple tables get 0s for relative column widths (i.e., use default) - return $ Table c a (replicate (length a) 0) h l + return (aligns, replicate (length aligns) 0, heads', lines') -- 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 :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block + -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) multilineTable headless = - tableWith (multilineTableHeader headless) multilineRow blanklines tableFooter tableCaption + tableWith (multilineTableHeader headless) multilineRow blanklines tableFooter multilineTableHeader :: Bool -- ^ Headerless table - -> GenParser Char ParserState ([[Block]], [Alignment], [Int]) + -> MarkdownParser (F [Blocks], [Alignment], [Int]) multilineTableHeader headless = try $ do if headless then return '\n' else tableSep >>~ notFollowedBy blankline rawContent <- if headless - then return $ repeat "" + then return $ repeat "" else many1 (notFollowedBy tableSep >> many1Till anyChar newline) initSp <- nonindentSpaces @@ -872,54 +1045,207 @@ multilineTableHeader headless = try $ do rawHeadsList <- if headless then liftM (map (:[]) . tail . splitStringByIndices (init indices)) $ lookAhead anyLine - else return $ transpose $ map + else return $ transpose $ map (\ln -> tail $ splitStringByIndices (init indices) ln) rawContent let aligns = zipWith alignType rawHeadsList lengths let rawHeads = if headless then replicate (length dashes) "" else map (intercalate " ") rawHeadsList - heads <- mapM (parseFromString (many plain)) $ - map removeLeadingTrailingSpace rawHeads + heads <- fmap sequence $ + mapM (parseFromString (mconcat <$> many plain)) $ + map trim rawHeads return (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 :: [String] - -> Int - -> Alignment -alignType [] _ = AlignDefault -alignType strLst len = - let nonempties = filter (not . null) $ map removeTrailingSpace strLst - (leftSpace, rightSpace) = - case sortBy (comparing length) nonempties of - (x:_) -> (head x `elem` " \t", length x < len) - [] -> (False, False) - in case (leftSpace, rightSpace) of - (True, False) -> AlignRight - (False, True) -> AlignLeft - (True, True) -> AlignCenter - (False, False) -> AlignDefault - +-- 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 :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block -gridTable = gridTableWith block tableCaption + -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +gridTable headless = + tableWith (gridTableHeader headless) gridTableRow + (gridTableSep '-') gridTableFooter + +gridTableSplitLine :: [Int] -> String -> [String] +gridTableSplitLine indices line = map removeFinalBar $ tail $ + splitStringByIndices (init indices) $ trimr line + +gridPart :: Char -> Parser [Char] st (Int, Int) +gridPart ch = do + dashes <- many1 (char ch) + char '+' + return (length dashes, length dashes + 1) + +gridDashedLines :: Char -> Parser [Char] st [(Int,Int)] +gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) >>~ blankline + +removeFinalBar :: String -> String +removeFinalBar = + reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse + +-- | Separator between rows of grid table. +gridTableSep :: Char -> MarkdownParser Char +gridTableSep ch = try $ gridDashedLines ch >> return '\n' + +-- | Parse header for a grid table. +gridTableHeader :: Bool -- ^ Headerless table + -> MarkdownParser (F [Blocks], [Alignment], [Int]) +gridTableHeader headless = try $ do + optional blanklines + dashes <- gridDashedLines '-' + rawContent <- if headless + then return $ repeat "" + else many1 + (notFollowedBy (gridTableSep '=') >> char '|' >> + many1Till anyChar newline) + if headless + then return () + else gridTableSep '=' >> return () + let lines' = map snd dashes + let indices = scanl (+) 0 lines' + let aligns = replicate (length lines') AlignDefault + -- RST does not have a notion of alignments + let rawHeads = if headless + then replicate (length dashes) "" + else map (intercalate " ") $ transpose + $ map (gridTableSplitLine indices) rawContent + heads <- fmap sequence $ mapM (parseFromString block) $ + map trim rawHeads + return (heads, aligns, indices) + +gridTableRawLine :: [Int] -> MarkdownParser [String] +gridTableRawLine indices = do + char '|' + line <- many1Till anyChar newline + return (gridTableSplitLine indices line) + +-- | Parse row of grid table. +gridTableRow :: [Int] + -> MarkdownParser (F [Blocks]) +gridTableRow indices = do + colLines <- many1 (gridTableRawLine indices) + let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ + transpose colLines + fmap compactify' <$> fmap sequence (mapM (parseFromString block) cols) + +removeOneLeadingSpace :: [String] -> [String] +removeOneLeadingSpace xs = + if all startsWithSpace xs + then map (drop 1) xs + else xs + where startsWithSpace "" = True + startsWithSpace (y:_) = y == ' ' + +-- | Parse footer for a grid table. +gridTableFooter :: MarkdownParser [Char] +gridTableFooter = blanklines + +pipeTable :: MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +pipeTable = try $ do + let pipeBreak = nonindentSpaces *> optional (char '|') *> + pipeTableHeaderPart `sepBy1` sepPipe <* + optional (char '|') <* blankline + (heads,aligns) <- try ( pipeBreak >>= \als -> + return (return $ replicate (length als) mempty, als)) + <|> ( pipeTableRow >>= \row -> pipeBreak >>= \als -> + + return (row, als) ) + lines' <- sequence <$> many1 pipeTableRow + blanklines + let widths = replicate (length aligns) 0.0 + return $ (aligns, widths, heads, lines') -table :: GenParser Char ParserState Block -table = multilineTable False <|> simpleTable True <|> - simpleTable False <|> multilineTable True <|> - gridTable False <|> gridTable True <?> "table" +sepPipe :: MarkdownParser () +sepPipe = try $ do + char '|' <|> char '+' + notFollowedBy blankline --- +-- parse a row, also returning probable alignments for org-table cells +pipeTableRow :: MarkdownParser (F [Blocks]) +pipeTableRow = do + nonindentSpaces + optional (char '|') + let cell = mconcat <$> + many (notFollowedBy (blankline <|> char '|') >> inline) + first <- cell + sepPipe + rest <- cell `sepBy1` sepPipe + optional (char '|') + blankline + let cells = sequence (first:rest) + return $ do + cells' <- cells + return $ map + (\ils -> + case trimInlines ils of + ils' | B.isNull ils' -> mempty + | otherwise -> B.plain $ ils') cells' + +pipeTableHeaderPart :: Parser [Char] st Alignment +pipeTableHeaderPart = do + left <- optionMaybe (char ':') + many1 (char '-') + right <- optionMaybe (char ':') + return $ + case (left,right) of + (Nothing,Nothing) -> AlignDefault + (Just _,Nothing) -> AlignLeft + (Nothing,Just _) -> AlignRight + (Just _,Just _) -> AlignCenter + +-- Succeed only if current line contains a pipe. +scanForPipe :: Parser [Char] st () +scanForPipe = lookAhead (manyTill (satisfy (/='\n')) (char '|')) >> return () + +-- | Parse a table using 'headerParser', 'rowParser', +-- 'lineParser', and 'footerParser'. Variant of the version in +-- Text.Pandoc.Parsing. +tableWith :: MarkdownParser (F [Blocks], [Alignment], [Int]) + -> ([Int] -> MarkdownParser (F [Blocks])) + -> MarkdownParser sep + -> MarkdownParser end + -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +tableWith headerParser rowParser lineParser footerParser = try $ do + (heads, aligns, indices) <- headerParser + lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser + footerParser + numColumns <- getOption readerColumns + let widths = if (indices == []) + then replicate (length aligns) 0.0 + else widthsFromIndices numColumns indices + return $ (aligns, widths, heads, lines') + +table :: MarkdownParser (F Blocks) +table = try $ do + frontCaption <- option Nothing (Just <$> tableCaption) + (aligns, widths, heads, lns) <- + try (guardEnabled Ext_pipe_tables >> scanForPipe >> pipeTable) <|> + try (guardEnabled Ext_multiline_tables >> + multilineTable False) <|> + try (guardEnabled Ext_simple_tables >> + (simpleTable True <|> simpleTable False)) <|> + try (guardEnabled Ext_multiline_tables >> + multilineTable True) <|> + try (guardEnabled Ext_grid_tables >> + (gridTable False <|> gridTable True)) <?> "table" + optional blanklines + caption <- case frontCaption of + Nothing -> option (return mempty) tableCaption + Just c -> return c + return $ do + caption' <- caption + heads' <- heads + lns' <- lns + return $ B.table caption' (zip aligns widths) heads' lns' + +-- -- inline -- -inline :: GenParser Char ParserState Inline -inline = choice inlineParsers <?> "inline" - -inlineParsers :: [GenParser Char ParserState Inline] -inlineParsers = [ whitespace +inline :: MarkdownParser (F Inlines) +inline = choice [ whitespace + , bareURL , str , endline , code @@ -927,8 +1253,8 @@ inlineParsers = [ whitespace , strong , emph , note - , link , cite + , link , image , math , strikeout @@ -940,171 +1266,174 @@ inlineParsers = [ whitespace , escapedChar , rawLaTeXInline' , exampleRef - , smartPunctuation inline - , charRef + , smart + , return . B.singleton <$> charRef , symbol - , ltSign ] + , ltSign + ] <?> "inline" -escapedChar' :: GenParser Char ParserState Char +escapedChar' :: MarkdownParser Char escapedChar' = try $ do char '\\' - state <- getState - if stateStrict state - then oneOf "\\`*_{}[]()>#+-.!~" - else satisfy (not . isAlphaNum) + (guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum)) + <|> oneOf "\\`*_{}[]()>#+-.!~\"" -escapedChar :: GenParser Char ParserState Inline +escapedChar :: MarkdownParser (F Inlines) escapedChar = do result <- escapedChar' - return $ case result of - ' ' -> Str "\160" -- "\ " is a nonbreaking space - '\n' -> LineBreak -- "\[newline]" is a linebreak - _ -> Str [result] + case result of + ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space + '\n' -> guardEnabled Ext_escaped_line_breaks >> + return (return B.linebreak) -- "\[newline]" is a linebreak + _ -> return $ return $ B.str [result] -ltSign :: GenParser Char ParserState Inline +ltSign :: MarkdownParser (F Inlines) ltSign = do - st <- getState - if stateStrict st - then char '<' - else notFollowedBy' rawHtmlBlocks >> char '<' -- unless it starts html - return $ Str ['<'] + guardDisabled Ext_raw_html + <|> guardDisabled Ext_markdown_in_html_blocks + <|> (notFollowedBy' rawHtmlBlocks >> return ()) + char '<' + return $ return $ B.str "<" -exampleRef :: GenParser Char ParserState Inline +exampleRef :: MarkdownParser (F Inlines) exampleRef = try $ do + guardEnabled Ext_example_lists char '@' lab <- many1 (alphaNum <|> oneOf "-_") - -- We just return a Str. These are replaced with numbers - -- later. See the end of parseMarkdown. - return $ Str $ '@' : lab - -symbol :: GenParser Char ParserState Inline -symbol = do + return $ do + st <- askF + return $ case M.lookup lab (stateExamples st) of + Just n -> B.str (show n) + Nothing -> B.str ('@':lab) + +symbol :: MarkdownParser (F Inlines) +symbol = do result <- noneOf "<\\\n\t " <|> try (do lookAhead $ char '\\' - notFollowedBy' rawTeXBlock + notFollowedBy' (() <$ rawTeXBlock) char '\\') - return $ Str [result] + return $ return $ B.str [result] -- parses inline code, between n `s and n `s -code :: GenParser Char ParserState Inline -code = try $ do +code :: MarkdownParser (F Inlines) +code = try $ do starts <- many1 (char '`') skipSpaces result <- many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|> (char '\n' >> notFollowedBy' blankline >> return " ")) - (try (skipSpaces >> count (length starts) (char '`') >> + (try (skipSpaces >> count (length starts) (char '`') >> notFollowedBy (char '`'))) - attr <- option ([],[],[]) (try $ optional whitespace >> attributes) - return $ Code attr $ removeLeadingTrailingSpace $ concat result - -mathWord :: GenParser Char st [Char] -mathWord = liftM concat $ many1 mathChunk - -mathChunk :: GenParser Char st [Char] -mathChunk = do char '\\' - c <- anyChar - return ['\\',c] - <|> many1 (satisfy $ \c -> not (isBlank c || c == '\\' || c == '$')) - -math :: GenParser Char ParserState Inline -math = (mathDisplay >>= applyMacros' >>= return . Math DisplayMath) - <|> (mathInline >>= applyMacros' >>= return . Math InlineMath) - -mathDisplay :: GenParser Char ParserState String -mathDisplay = try $ do - failIfStrict - string "$$" - many1Till (noneOf "\n" <|> (newline >>~ notFollowedBy' blankline)) (try $ string "$$") - -mathInline :: GenParser Char ParserState String -mathInline = try $ do - failIfStrict - char '$' + attr <- option ([],[],[]) (try $ guardEnabled Ext_inline_code_attributes >> + optional whitespace >> attributes) + return $ return $ B.codeWith attr $ trim $ concat result + +math :: MarkdownParser (F Inlines) +math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) + <|> (return . B.math <$> (mathInline >>= applyMacros')) + +mathDisplay :: MarkdownParser String +mathDisplay = + (guardEnabled Ext_tex_math_dollars >> mathDisplayWith "$$" "$$") + <|> (guardEnabled Ext_tex_math_single_backslash >> + mathDisplayWith "\\[" "\\]") + <|> (guardEnabled Ext_tex_math_double_backslash >> + mathDisplayWith "\\\\[" "\\\\]") + +mathDisplayWith :: String -> String -> MarkdownParser String +mathDisplayWith op cl = try $ do + string op + many1Till (noneOf "\n" <|> (newline >>~ notFollowedBy' blankline)) (try $ string cl) + +mathInline :: MarkdownParser String +mathInline = + (guardEnabled Ext_tex_math_dollars >> mathInlineWith "$" "$") + <|> (guardEnabled Ext_tex_math_single_backslash >> + mathInlineWith "\\(" "\\)") + <|> (guardEnabled Ext_tex_math_double_backslash >> + mathInlineWith "\\\\(" "\\\\)") + +mathInlineWith :: String -> String -> MarkdownParser String +mathInlineWith op cl = try $ do + string op notFollowedBy space - words' <- sepBy1 mathWord (many1 (spaceChar <|> (newline >>~ notFollowedBy' blankline))) - char '$' - notFollowedBy digit - return $ intercalate " " words' + words' <- many1Till (count 1 (noneOf "\n\\") + <|> (char '\\' >> anyChar >>= \c -> return ['\\',c]) + <|> count 1 newline <* notFollowedBy' blankline + *> return " ") + (try $ string cl) + notFollowedBy digit -- to prevent capture of $5 + return $ concat words' -- to avoid performance problems, treat 4 or more _ or * or ~ or ^ in a row -- as a literal rather than attempting to parse for emph/strong/strikeout/super/sub -fours :: GenParser Char st Inline +fours :: Parser [Char] st (F Inlines) fours = try $ do x <- char '*' <|> char '_' <|> char '~' <|> char '^' count 2 $ satisfy (==x) rest <- many1 (satisfy (==x)) - return $ Str (x:x:x:rest) + return $ return $ B.str (x:x:x:rest) -- | Parses a list of inlines between start and end delimiters. inlinesBetween :: (Show b) - => GenParser Char ParserState a - -> GenParser Char ParserState b - -> GenParser Char ParserState [Inline] + => MarkdownParser a + -> MarkdownParser b + -> MarkdownParser (F Inlines) inlinesBetween start end = - normalizeSpaces `liftM` try (start >> many1Till inner end) - where inner = innerSpace <|> (notFollowedBy' whitespace >> inline) + (trimInlinesF . mconcat) <$> try (start >> many1Till inner end) + where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline) innerSpace = try $ whitespace >>~ notFollowedBy' end --- This is used to prevent exponential blowups for things like: --- a**a*a**a*a**a*a**a*a**a*a**a*a** -nested :: GenParser Char ParserState a - -> GenParser Char ParserState a -nested p = do - nestlevel <- stateMaxNestingLevel `fmap` getState - guard $ nestlevel > 0 - updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 } - res <- p - updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } - return res - -emph :: GenParser Char ParserState Inline -emph = Emph `fmap` nested +emph :: MarkdownParser (F Inlines) +emph = fmap B.emph <$> nested (inlinesBetween starStart starEnd <|> inlinesBetween ulStart ulEnd) where starStart = char '*' >> lookAhead nonspaceChar - starEnd = notFollowedBy' strong >> char '*' + starEnd = notFollowedBy' (() <$ strong) >> char '*' ulStart = char '_' >> lookAhead nonspaceChar - ulEnd = notFollowedBy' strong >> char '_' + ulEnd = notFollowedBy' (() <$ strong) >> char '_' -strong :: GenParser Char ParserState Inline -strong = Strong `liftM` nested +strong :: MarkdownParser (F Inlines) +strong = fmap B.strong <$> nested (inlinesBetween starStart starEnd <|> inlinesBetween ulStart ulEnd) where starStart = string "**" >> lookAhead nonspaceChar starEnd = try $ string "**" ulStart = string "__" >> lookAhead nonspaceChar ulEnd = try $ string "__" -strikeout :: GenParser Char ParserState Inline -strikeout = Strikeout `liftM` - (failIfStrict >> inlinesBetween strikeStart strikeEnd) +strikeout :: MarkdownParser (F Inlines) +strikeout = fmap B.strikeout <$> + (guardEnabled Ext_strikeout >> inlinesBetween strikeStart strikeEnd) where strikeStart = string "~~" >> lookAhead nonspaceChar >> notFollowedBy (char '~') strikeEnd = try $ string "~~" -superscript :: GenParser Char ParserState Inline -superscript = failIfStrict >> enclosed (char '^') (char '^') - (notFollowedBy spaceChar >> inline) >>= -- may not contain Space - return . Superscript +superscript :: MarkdownParser (F Inlines) +superscript = fmap B.superscript <$> try (do + guardEnabled Ext_superscript + char '^' + mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '^')) -subscript :: GenParser Char ParserState Inline -subscript = failIfStrict >> enclosed (char '~') (char '~') - (notFollowedBy spaceChar >> inline) >>= -- may not contain Space - return . Subscript +subscript :: MarkdownParser (F Inlines) +subscript = fmap B.subscript <$> try (do + guardEnabled Ext_subscript + char '~' + mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '~')) -whitespace :: GenParser Char ParserState Inline -whitespace = spaceChar >> - ( (spaceChar >> skipMany spaceChar >> option Space (endline >> return LineBreak)) - <|> (skipMany spaceChar >> return Space) ) <?> "whitespace" +whitespace :: MarkdownParser (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 :: GenParser Char st Char +nonEndline :: Parser [Char] st Char nonEndline = satisfy (/='\n') -str :: GenParser Char ParserState Inline +str :: MarkdownParser (F Inlines) str = do - smart <- stateSmart `fmap` getState + isSmart <- readerSmart . stateOptions <$> getState a <- alphaNum as <- many $ alphaNum - <|> (try $ char '_' >>~ lookAhead alphaNum) - <|> if smart + <|> (guardEnabled Ext_intraword_underscores >> + try (char '_' >>~ lookAhead alphaNum)) + <|> if isSmart then (try $ satisfy (\c -> c == '\'' || c == '\x2019') >> lookAhead alphaNum >> return '\x2019') -- for things like l'aide @@ -1113,15 +1442,16 @@ str = do updateState $ \s -> s{ stateLastStrPos = Just pos } let result = a:as let spacesToNbr = map (\c -> if c == ' ' then '\160' else c) - if smart + if isSmart then case likelyAbbrev result of - [] -> return $ Str result + [] -> return $ return $ B.str result xs -> choice (map (\x -> try (string x >> oneOf " \n" >> lookAhead alphaNum >> - return (Str $ result ++ spacesToNbr x ++ "\160"))) xs) - <|> (return $ Str result) - else return $ Str result + return (return $ B.str + $ result ++ spacesToNbr x ++ "\160"))) xs) + <|> (return $ return $ B.str result) + else return $ return $ B.str result -- | if the string matches the beginning of an abbreviation (before -- the first period, return strings that would finish the abbreviation. @@ -1136,136 +1466,145 @@ likelyAbbrev x = in map snd $ filter (\(y,_) -> y == x) abbrPairs -- an endline character that can be treated as a space, not a structural break -endline :: GenParser Char ParserState Inline +endline :: MarkdownParser (F Inlines) endline = try $ do newline notFollowedBy blankline - st <- getState - when (stateStrict st) $ do - notFollowedBy emailBlockQuoteStart - notFollowedBy (char '#') -- atx header + guardEnabled Ext_blank_before_blockquote <|> notFollowedBy emailBlockQuoteStart + guardEnabled Ext_blank_before_header <|> notFollowedBy (char '#') -- atx header -- parse potential list-starts differently if in a list: + st <- getState when (stateParserContext st == ListItemState) $ do notFollowedBy' bulletListStart notFollowedBy' anyOrderedListStart - return Space + (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak)) + <|> (return $ return B.space) -- -- links -- -- a reference label for a link -reference :: GenParser Char ParserState [Inline] +reference :: MarkdownParser (F Inlines, String) reference = do notFollowedBy' (string "[^") -- footnote reference - result <- inlinesInBalancedBrackets inline - return $ normalizeSpaces result + withRaw $ trimInlinesF <$> inlinesInBalancedBrackets -- source for a link, with optional title -source :: GenParser Char ParserState (String, [Char]) -source = - (try $ charsInBalanced '(' ')' litChar >>= parseFromString source') <|> - -- the following is needed for cases like: [ref](/url(a). - (enclosed (char '(') (char ')') litChar >>= parseFromString source') - --- auxiliary function for source -source' :: GenParser Char ParserState (String, [Char]) -source' = do +source :: MarkdownParser (String, String) +source = do + char '(' skipSpaces - let nl = char '\n' >>~ notFollowedBy blankline - let sourceURL = liftM unwords $ many $ try $ do - notFollowedBy' linkTitle - skipMany spaceChar - optional nl - skipMany spaceChar - many1 $ escapedChar' <|> satisfy (not . isBlank) + let urlChunk = try $ notFollowedBy (oneOf "\"')") >> + (charsInBalanced '(' ')' litChar <|> count 1 litChar) + let sourceURL = (unwords . words . concat) <$> many urlChunk let betweenAngles = try $ - char '<' >> manyTill (escapedChar' <|> noneOf ">\n" <|> nl) (char '>') + char '<' >> manyTill litChar (char '>') src <- try betweenAngles <|> sourceURL - tit <- option "" linkTitle + tit <- option "" $ try $ spnl >> linkTitle skipSpaces - eof - return (escapeURI $ removeTrailingSpace src, tit) + char ')' + return (escapeURI $ trimr src, tit) -linkTitle :: GenParser Char ParserState String -linkTitle = try $ do - (many1 spaceChar >> option '\n' newline) <|> newline - skipSpaces - delim <- oneOf "'\"" - tit <- manyTill litChar (try (char delim >> skipSpaces >> eof)) - return $ fromEntities tit +linkTitle :: MarkdownParser String +linkTitle = fromEntities <$> (quotedTitle '"' <|> quotedTitle '\'') -link :: GenParser Char ParserState Inline +link :: MarkdownParser (F Inlines) link = try $ do - lab <- reference - (src, tit) <- source <|> referenceLink lab - return $ Link (delinkify lab) (src, tit) - -delinkify :: [Inline] -> [Inline] -delinkify = bottomUp $ concatMap go - where go (Link lab _) = lab - go x = [x] + st <- getState + guard $ stateAllowLinks st + setState $ st{ stateAllowLinks = False } + (lab,raw) <- reference + setState $ st{ stateAllowLinks = True } + regLink B.link lab <|> referenceLink B.link (lab,raw) + +regLink :: (String -> String -> Inlines -> Inlines) + -> F Inlines -> MarkdownParser (F Inlines) +regLink constructor lab = try $ do + (src, tit) <- source + return $ constructor src tit <$> lab -- a link like [this][ref] or [this][] or [this] -referenceLink :: [Inline] - -> GenParser Char ParserState (String, [Char]) -referenceLink lab = do - ref <- option [] (try (optional (char ' ') >> - optional (newline >> skipSpaces) >> reference)) - let ref' = if null ref then lab else ref - state <- getState - case lookupKeySrc (stateKeys state) (toKey ref') of - Nothing -> fail "no corresponding key" - Just target -> return target +referenceLink :: (String -> String -> Inlines -> Inlines) + -> (F Inlines, String) -> MarkdownParser (F Inlines) +referenceLink constructor (lab, raw) = do + (ref,raw') <- try (optional (char ' ') >> + optional (newline >> skipSpaces) >> + reference) <|> return (mempty, "") + let labIsRef = raw' == "" || raw' == "[]" + let key = toKey $ if labIsRef then raw else raw' + let dropRB (']':xs) = xs + dropRB xs = xs + let dropLB ('[':xs) = xs + dropLB xs = xs + let dropBrackets = reverse . dropRB . reverse . dropLB + fallback <- parseFromString (mconcat <$> many inline) $ dropBrackets raw + implicitHeaderRefs <- option False $ + True <$ guardEnabled Ext_implicit_header_references + return $ do + keys <- asksF stateKeys + case M.lookup key keys of + Nothing -> do + headers <- asksF stateHeaders + let ref' = B.toList $ runF (if labIsRef then lab else ref) + defaultParserState + if implicitHeaderRefs && ref' `elem` headers + then do + let src = '#' : uniqueIdent ref' [] + constructor src "" <$> lab + else (\x -> B.str "[" <> x <> B.str "]" <> B.str raw') <$> fallback + Just (src,tit) -> constructor src tit <$> lab + +bareURL :: MarkdownParser (F Inlines) +bareURL = try $ do + guardEnabled Ext_autolink_bare_uris + (orig, src) <- uri <|> emailAddress + return $ return $ B.link src "" (B.str orig) -autoLink :: GenParser Char ParserState Inline +autoLink :: MarkdownParser (F Inlines) autoLink = try $ do char '<' (orig, src) <- uri <|> emailAddress char '>' - st <- getState - return $ if stateStrict st - then Link [Str orig] (src, "") - else Link [Code ("",["url"],[]) orig] (src, "") + return $ return $ B.link src "" (B.str orig) -image :: GenParser Char ParserState Inline +image :: MarkdownParser (F Inlines) image = try $ do char '!' - lab <- reference - (src, tit) <- source <|> referenceLink lab - return $ Image lab (src,tit) + (lab,raw) <- reference + regLink B.image lab <|> referenceLink B.image (lab,raw) -note :: GenParser Char ParserState Inline +note :: MarkdownParser (F Inlines) note = try $ do - failIfStrict + guardEnabled Ext_footnotes ref <- noteMarker - state <- getState - let notes = stateNotes state - case lookup ref notes of - Nothing -> fail "note not found" - 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 do not work. - updateState $ \st -> st{ stateNotes = [] } - contents <- parseFromString parseBlocks raw - updateState $ \st -> st{ stateNotes = notes } - return $ Note contents - -inlineNote :: GenParser Char ParserState Inline + return $ do + notes <- asksF stateNotes' + case lookup ref notes of + Nothing -> return $ B.str $ "[^" ++ ref ++ "]" + Just 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' = [] } + return $ B.note contents' + +inlineNote :: MarkdownParser (F Inlines) inlineNote = try $ do - failIfStrict + guardEnabled Ext_inline_notes char '^' - contents <- inlinesInBalancedBrackets inline - return $ Note [Para contents] + contents <- inlinesInBalancedBrackets + return $ B.note . B.para <$> contents -rawLaTeXInline' :: GenParser Char ParserState Inline +rawLaTeXInline' :: MarkdownParser (F Inlines) rawLaTeXInline' = try $ do - failIfStrict + guardEnabled Ext_raw_tex lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env RawInline _ s <- rawLaTeXInline - return $ RawInline "tex" s -- "tex" because it might be context or latex + return $ return $ B.rawInline "tex" s + -- "tex" because it might be context or latex -rawConTeXtEnvironment :: GenParser Char st String +rawConTeXtEnvironment :: Parser [Char] st String rawConTeXtEnvironment = try $ do string "\\start" completion <- inBrackets (letter <|> digit <|> spaceChar) @@ -1274,37 +1613,33 @@ rawConTeXtEnvironment = try $ do (try $ string "\\stop" >> string completion) return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion -inBrackets :: (GenParser Char st Char) -> GenParser Char st String +inBrackets :: (Parser [Char] st Char) -> Parser [Char] st String inBrackets parser = do char '[' contents <- many parser char ']' return $ "[" ++ contents ++ "]" -rawHtmlInline :: GenParser Char ParserState Inline +rawHtmlInline :: MarkdownParser (F Inlines) rawHtmlInline = do - st <- getState - (_,result) <- if stateStrict st - then htmlTag (not . isTextTag) - else htmlTag isInlineTag - return $ RawInline "html" result + guardEnabled Ext_raw_html + mdInHtml <- option False $ + guardEnabled Ext_markdown_in_html_blocks >> return True + (_,result) <- if mdInHtml + then htmlTag isInlineTag + else htmlTag (not . isTextTag) + return $ return $ B.rawInline "html" result -- Citations -cite :: GenParser Char ParserState Inline +cite :: MarkdownParser (F Inlines) cite = do - failIfStrict + guardEnabled Ext_citations + getOption readerReferences >>= guard . not . null citations <- textualCite <|> normalCite - return $ Cite citations [] + return $ flip B.cite mempty <$> citations -spnl :: GenParser Char st () -spnl = try $ do - skipSpaces - optional newline - skipSpaces - notFollowedBy (char '\n') - -textualCite :: GenParser Char ParserState [Citation] +textualCite :: MarkdownParser (F [Citation]) textualCite = try $ do (_, key) <- citeKey let first = Citation{ citationId = key @@ -1314,22 +1649,25 @@ textualCite = try $ do , citationNoteNum = 0 , citationHash = 0 } - rest <- option [] $ try $ spnl >> normalCite - if null rest - then option [first] $ bareloc first - else return $ first : rest + mbrest <- option Nothing $ try $ spnl >> Just <$> normalCite + case mbrest of + Just rest -> return $ (first:) <$> rest + Nothing -> option (return [first]) $ bareloc first -bareloc :: Citation -> GenParser Char ParserState [Citation] +bareloc :: Citation -> MarkdownParser (F [Citation]) bareloc c = try $ do spnl char '[' suff <- suffix - rest <- option [] $ try $ char ';' >> citeList + rest <- option (return []) $ try $ char ';' >> citeList spnl char ']' - return $ c{ citationSuffix = suff } : rest + return $ do + suff' <- suff + rest' <- rest + return $ c{ citationSuffix = B.toList suff' } : rest' -normalCite :: GenParser Char ParserState [Citation] +normalCite :: MarkdownParser (F [Citation]) normalCite = try $ do char '[' spnl @@ -1338,42 +1676,45 @@ normalCite = try $ do char ']' return citations -citeKey :: GenParser Char ParserState (Bool, String) +citeKey :: MarkdownParser (Bool, String) citeKey = try $ do suppress_author <- option False (char '-' >> return True) char '@' first <- letter let internal p = try $ p >>~ lookAhead (letter <|> digit) - rest <- many $ letter <|> digit <|> internal (oneOf ":.#$%&-_?<>~") + rest <- many $ letter <|> digit <|> internal (oneOf ":.#$%&-_?<>~/") let key = first:rest - st <- getState - guard $ key `elem` stateCitations st + citations' <- map CSL.refId <$> getOption readerReferences + guard $ key `elem` citations' return (suppress_author, key) -suffix :: GenParser Char ParserState [Inline] +suffix :: MarkdownParser (F Inlines) suffix = try $ do hasSpace <- option False (notFollowedBy nonspaceChar >> return True) spnl - rest <- liftM normalizeSpaces $ many $ notFollowedBy (oneOf ";]") >> inline + rest <- trimInlinesF . mconcat <$> many (notFollowedBy (oneOf ";]") >> inline) return $ if hasSpace - then Space : rest + then (B.space <>) <$> rest else rest -prefix :: GenParser Char ParserState [Inline] -prefix = liftM normalizeSpaces $ +prefix :: MarkdownParser (F Inlines) +prefix = trimInlinesF . mconcat <$> manyTill inline (char ']' <|> liftM (const ']') (lookAhead citeKey)) -citeList :: GenParser Char ParserState [Citation] -citeList = sepBy1 citation (try $ char ';' >> spnl) +citeList :: MarkdownParser (F [Citation]) +citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl) -citation :: GenParser Char ParserState Citation +citation :: MarkdownParser (F Citation) citation = try $ do pref <- prefix (suppress_author, key) <- citeKey suff <- suffix - return $ Citation{ citationId = key - , citationPrefix = pref - , citationSuffix = suff + 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 @@ -1381,3 +1722,22 @@ citation = try $ do , citationHash = 0 } +smart :: MarkdownParser (F Inlines) +smart = do + getOption readerSmart >>= guard + doubleQuoted <|> singleQuoted <|> + choice (map (return . B.singleton <$>) [apostrophe, dash, ellipses]) + +singleQuoted :: MarkdownParser (F Inlines) +singleQuoted = try $ do + singleQuoteStart + withQuoteContext InSingleQuote $ + fmap B.singleQuoted . trimInlinesF . mconcat <$> + many1Till inline singleQuoteEnd + +doubleQuoted :: MarkdownParser (F Inlines) +doubleQuoted = try $ do + doubleQuoteStart + withQuoteContext InDoubleQuote $ + fmap B.doubleQuoted . trimInlinesF . mconcat <$> + many1Till inline doubleQuoteEnd diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs new file mode 100644 index 000000000..434f67646 --- /dev/null +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -0,0 +1,593 @@ +{-# LANGUAGE RelaxedPolyRec #-} -- needed for inlinesBetween on GHC < 7 +{- + Copyright (C) 2012 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.Readers.MediaWiki + Copyright : Copyright (C) 2012 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of mediawiki text to 'Pandoc' document. +-} +{- +TODO: +_ correctly handle tables within tables +_ parse templates? +-} +module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where + +import Text.Pandoc.Definition +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Builder (Inlines, Blocks, trimInlines, (<>)) +import Text.Pandoc.Options +import Text.Pandoc.Readers.HTML ( htmlTag, isBlockTag, isCommentTag ) +import Text.Pandoc.XML ( fromEntities ) +import Text.Pandoc.Parsing hiding ( nested ) +import Text.Pandoc.Generic ( bottomUp ) +import Text.Pandoc.Shared ( stripTrailingNewlines, safeRead ) +import Data.Monoid (mconcat, mempty) +import Control.Applicative ((<$>), (<*), (*>), (<$)) +import Control.Monad +import Data.List (intersperse, intercalate, isPrefixOf ) +import Text.HTML.TagSoup +import Data.Sequence (viewl, ViewL(..), (<|)) +import Data.Char (isDigit) + +-- | Read mediawiki from an input string and return a Pandoc document. +readMediaWiki :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Pandoc +readMediaWiki opts s = + case runParser parseMediaWiki MWState{ mwOptions = opts + , mwMaxNestingLevel = 4 + , mwNextLinkNumber = 1 + , mwCategoryLinks = [] + } + "source" (s ++ "\n") of + Left err' -> error $ "\nError:\n" ++ show err' + Right result -> result + +data MWState = MWState { mwOptions :: ReaderOptions + , mwMaxNestingLevel :: Int + , mwNextLinkNumber :: Int + , mwCategoryLinks :: [Inlines] + } + +type MWParser = Parser [Char] MWState + +-- +-- auxiliary functions +-- + +-- This is used to prevent exponential blowups for things like: +-- ''a'''a''a'''a''a'''a''a'''a +nested :: MWParser a -> MWParser a +nested p = do + nestlevel <- mwMaxNestingLevel `fmap` getState + guard $ nestlevel > 0 + updateState $ \st -> st{ mwMaxNestingLevel = mwMaxNestingLevel st - 1 } + res <- p + updateState $ \st -> st{ mwMaxNestingLevel = nestlevel } + return res + +specialChars :: [Char] +specialChars = "'[]<=&*{}|\"" + +spaceChars :: [Char] +spaceChars = " \n\t" + +sym :: String -> MWParser () +sym s = () <$ try (string s) + +newBlockTags :: [String] +newBlockTags = ["haskell","syntaxhighlight","source","gallery","references"] + +isBlockTag' :: Tag String -> Bool +isBlockTag' tag@(TagOpen t _) = (isBlockTag tag || t `elem` newBlockTags) && + t `notElem` eitherBlockOrInline +isBlockTag' tag@(TagClose t) = (isBlockTag tag || t `elem` newBlockTags) && + t `notElem` eitherBlockOrInline +isBlockTag' tag = isBlockTag tag + +isInlineTag' :: Tag String -> Bool +isInlineTag' (TagComment _) = True +isInlineTag' t = not (isBlockTag' t) + +eitherBlockOrInline :: [String] +eitherBlockOrInline = ["applet", "button", "del", "iframe", "ins", + "map", "area", "object"] + +htmlComment :: MWParser () +htmlComment = () <$ htmlTag isCommentTag + +inlinesInTags :: String -> MWParser Inlines +inlinesInTags tag = try $ do + (_,raw) <- htmlTag (~== TagOpen tag []) + if '/' `elem` raw -- self-closing tag + then return mempty + else trimInlines . mconcat <$> + manyTill inline (htmlTag (~== TagClose tag)) + +blocksInTags :: String -> MWParser Blocks +blocksInTags tag = try $ do + (_,raw) <- htmlTag (~== TagOpen tag []) + if '/' `elem` raw -- self-closing tag + then return mempty + else mconcat <$> manyTill block (htmlTag (~== TagClose tag)) + +charsInTags :: String -> MWParser [Char] +charsInTags tag = try $ do + (_,raw) <- htmlTag (~== TagOpen tag []) + if '/' `elem` raw -- self-closing tag + then return "" + else manyTill anyChar (htmlTag (~== TagClose tag)) + +-- +-- main parser +-- + +parseMediaWiki :: MWParser Pandoc +parseMediaWiki = do + bs <- mconcat <$> many block + spaces + eof + categoryLinks <- reverse . mwCategoryLinks <$> getState + let categories = if null categoryLinks + then mempty + else B.para $ mconcat $ intersperse B.space categoryLinks + return $ B.doc $ bs <> categories + +-- +-- block parsers +-- + +block :: MWParser Blocks +block = mempty <$ skipMany1 blankline + <|> table + <|> header + <|> hrule + <|> orderedList + <|> bulletList + <|> definitionList + <|> mempty <$ try (spaces *> htmlComment) + <|> preformatted + <|> blockTag + <|> (B.rawBlock "mediawiki" <$> template) + <|> para + +para :: MWParser Blocks +para = B.para . trimInlines . mconcat <$> many1 inline + +table :: MWParser Blocks +table = do + tableStart + styles <- manyTill anyChar newline + let tableWidth = case lookup "width" $ parseAttrs styles of + Just w -> maybe 1.0 id $ parseWidth w + Nothing -> 1.0 + caption <- option mempty tableCaption + optional rowsep + hasheader <- option False $ True <$ (lookAhead (char '!')) + (cellspecs',hdr) <- unzip <$> tableRow + let widths = map ((tableWidth *) . snd) cellspecs' + let restwidth = tableWidth - sum widths + let zerocols = length $ filter (==0.0) widths + let defaultwidth = if zerocols == 0 || zerocols == length widths + then 0.0 + else restwidth / fromIntegral zerocols + let widths' = map (\w -> if w == 0 then defaultwidth else w) widths + let cellspecs = zip (map fst cellspecs') widths' + rows' <- many $ try $ rowsep *> (map snd <$> tableRow) + tableEnd + let cols = length hdr + let (headers,rows) = if hasheader + then (hdr, rows') + else (replicate cols mempty, hdr:rows') + return $ B.table caption cellspecs headers rows + +parseAttrs :: String -> [(String,String)] +parseAttrs s = case parse (many parseAttr) "attributes" s of + Right r -> r + Left _ -> [] + +parseAttr :: Parser String () (String, String) +parseAttr = try $ do + skipMany spaceChar + k <- many1 letter + char '=' + char '"' + v <- many1Till anyChar (char '"') + return (k,v) + +tableStart :: MWParser () +tableStart = try $ guardColumnOne *> sym "{|" + +tableEnd :: MWParser () +tableEnd = try $ guardColumnOne *> sym "|}" <* blanklines + +rowsep :: MWParser () +rowsep = try $ guardColumnOne *> sym "|-" <* blanklines + +cellsep :: MWParser () +cellsep = try $ + (guardColumnOne <* + ( (char '|' <* notFollowedBy (oneOf "-}+")) + <|> (char '!') + ) + ) + <|> (() <$ try (string "||")) + <|> (() <$ try (string "!!")) + +tableCaption :: MWParser Inlines +tableCaption = try $ do + guardColumnOne + sym "|+" + skipMany spaceChar + res <- manyTill anyChar newline >>= parseFromString (many inline) + return $ trimInlines $ mconcat res + +tableRow :: MWParser [((Alignment, Double), Blocks)] +tableRow = try $ many tableCell + +tableCell :: MWParser ((Alignment, Double), Blocks) +tableCell = try $ do + cellsep + skipMany spaceChar + attrs <- option [] $ try $ parseAttrs <$> + manyTill (satisfy (/='\n')) (char '|' <* notFollowedBy (char '|')) + skipMany spaceChar + ls <- concat <$> many (notFollowedBy (cellsep <|> rowsep <|> tableEnd) *> + ((snd <$> withRaw table) <|> count 1 anyChar)) + bs <- parseFromString (mconcat <$> many block) ls + let align = case lookup "align" attrs of + Just "left" -> AlignLeft + Just "right" -> AlignRight + Just "center" -> AlignCenter + _ -> AlignDefault + let width = case lookup "width" attrs of + Just xs -> maybe 0.0 id $ parseWidth xs + Nothing -> 0.0 + return ((align, width), bs) + +parseWidth :: String -> Maybe Double +parseWidth s = + case reverse s of + ('%':ds) | all isDigit ds -> safeRead ('0':'.':reverse ds) + _ -> Nothing + +template :: MWParser String +template = try $ do + string "{{" + notFollowedBy (char '{') + let chunk = template <|> variable <|> many1 (noneOf "{}") <|> count 1 anyChar + contents <- manyTill chunk (try $ string "}}") + return $ "{{" ++ concat contents ++ "}}" + +blockTag :: MWParser Blocks +blockTag = do + (tag, _) <- lookAhead $ htmlTag isBlockTag' + case tag of + TagOpen "blockquote" _ -> B.blockQuote <$> blocksInTags "blockquote" + TagOpen "pre" _ -> B.codeBlock . trimCode <$> charsInTags "pre" + TagOpen "syntaxhighlight" attrs -> syntaxhighlight "syntaxhighlight" attrs + TagOpen "source" attrs -> syntaxhighlight "source" attrs + TagOpen "haskell" _ -> B.codeBlockWith ("",["haskell"],[]) . trimCode <$> + charsInTags "haskell" + TagOpen "gallery" _ -> blocksInTags "gallery" + TagOpen "p" _ -> mempty <$ htmlTag (~== tag) + TagClose "p" -> mempty <$ htmlTag (~== tag) + _ -> B.rawBlock "html" . snd <$> htmlTag (~== tag) + +trimCode :: String -> String +trimCode ('\n':xs) = stripTrailingNewlines xs +trimCode xs = stripTrailingNewlines xs + +syntaxhighlight :: String -> [Attribute String] -> MWParser Blocks +syntaxhighlight tag attrs = try $ do + let mblang = lookup "lang" attrs + let mbstart = lookup "start" attrs + let mbline = lookup "line" attrs + let classes = maybe [] (:[]) mblang ++ maybe [] (const ["numberLines"]) mbline + let kvs = maybe [] (\x -> [("startFrom",x)]) mbstart + contents <- charsInTags tag + return $ B.codeBlockWith ("",classes,kvs) $ trimCode contents + +hrule :: MWParser Blocks +hrule = B.horizontalRule <$ try (string "----" *> many (char '-') *> newline) + +guardColumnOne :: MWParser () +guardColumnOne = getPosition >>= \pos -> guard (sourceColumn pos == 1) + +preformatted :: MWParser Blocks +preformatted = try $ do + guardColumnOne + char ' ' + let endline' = B.linebreak <$ (try $ newline <* char ' ') + let whitespace' = B.str <$> many1 ('\160' <$ spaceChar) + let spToNbsp ' ' = '\160' + spToNbsp x = x + let nowiki' = mconcat . intersperse B.linebreak . map B.str . + lines . fromEntities . map spToNbsp <$> try + (htmlTag (~== TagOpen "nowiki" []) *> + manyTill anyChar (htmlTag (~== TagClose "nowiki"))) + let inline' = whitespace' <|> endline' <|> nowiki' <|> inline + let strToCode (Str s) = Code ("",[],[]) s + strToCode x = x + B.para . bottomUp strToCode . mconcat <$> many1 inline' + +header :: MWParser Blocks +header = try $ do + guardColumnOne + eqs <- many1 (char '=') + let lev = length eqs + guard $ lev <= 6 + contents <- trimInlines . mconcat <$> manyTill inline (count lev $ char '=') + return $ B.header lev contents + +bulletList :: MWParser Blocks +bulletList = B.bulletList <$> + ( many1 (listItem '*') + <|> (htmlTag (~== TagOpen "ul" []) *> spaces *> many (listItem '*' <|> li) <* + optional (htmlTag (~== TagClose "ul"))) ) + +orderedList :: MWParser Blocks +orderedList = + (B.orderedList <$> many1 (listItem '#')) + <|> (B.orderedList <$> (htmlTag (~== TagOpen "ul" []) *> spaces *> + many (listItem '#' <|> li) <* + optional (htmlTag (~== TagClose "ul")))) + <|> do (tag,_) <- htmlTag (~== TagOpen "ol" []) + spaces + items <- many (listItem '#' <|> li) + optional (htmlTag (~== TagClose "ol")) + let start = maybe 1 id $ safeRead $ fromAttrib "start" tag + return $ B.orderedListWith (start, DefaultStyle, DefaultDelim) items + +definitionList :: MWParser Blocks +definitionList = B.definitionList <$> many1 defListItem + +defListItem :: MWParser (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 B.isNull terms + then many1 $ listItem ':' + else many $ listItem ':' + return (terms, defs) + +defListTerm :: MWParser Inlines +defListTerm = char ';' >> skipMany spaceChar >> manyTill anyChar newline >>= + parseFromString (trimInlines . mconcat <$> many inline) + +listStart :: Char -> MWParser () +listStart c = char c *> notFollowedBy listStartChar + +listStartChar :: MWParser Char +listStartChar = oneOf "*#;:" + +anyListStart :: MWParser Char +anyListStart = char '*' + <|> char '#' + <|> char ':' + <|> char ';' + +li :: MWParser Blocks +li = lookAhead (htmlTag (~== TagOpen "li" [])) *> + (firstParaToPlain <$> blocksInTags "li") <* spaces + +listItem :: Char -> MWParser Blocks +listItem c = try $ do + extras <- many (try $ char c <* lookAhead listStartChar) + if null extras + then listItem' c + else do + skipMany spaceChar + first <- concat <$> manyTill listChunk newline + rest <- many + (try $ string extras *> (concat <$> manyTill listChunk newline)) + contents <- parseFromString (many1 $ listItem' c) + (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. +listChunk :: MWParser String +listChunk = template <|> count 1 anyChar + +listItem' :: Char -> MWParser Blocks +listItem' c = try $ do + listStart c + skipMany spaceChar + first <- concat <$> manyTill listChunk newline + rest <- many (try $ char c *> lookAhead listStartChar *> + (concat <$> manyTill listChunk newline)) + parseFromString (firstParaToPlain . mconcat <$> many1 block) + $ 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 :: MWParser Inlines +inline = whitespace + <|> url + <|> str + <|> doubleQuotes + <|> strong + <|> emph + <|> image + <|> internalLink + <|> externalLink + <|> inlineTag + <|> B.singleton <$> charRef + <|> inlineHtml + <|> (B.rawInline "mediawiki" <$> variable) + <|> (B.rawInline "mediawiki" <$> template) + <|> special + +str :: MWParser Inlines +str = B.str <$> many1 (noneOf $ specialChars ++ spaceChars) + +variable :: MWParser String +variable = try $ do + string "{{{" + contents <- manyTill anyChar (try $ string "}}}") + return $ "{{{" ++ contents ++ "}}}" + +inlineTag :: MWParser Inlines +inlineTag = do + (tag, _) <- lookAhead $ htmlTag isInlineTag' + case tag of + TagOpen "ref" _ -> B.note . B.plain <$> inlinesInTags "ref" + TagOpen "nowiki" _ -> try $ do + (_,raw) <- htmlTag (~== tag) + if '/' `elem` raw + then return mempty + else B.text . fromEntities <$> + manyTill anyChar (htmlTag (~== TagClose "nowiki")) + TagOpen "br" _ -> B.linebreak <$ (htmlTag (~== TagOpen "br" []) -- 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 "math" _ -> B.math <$> charsInTags "math" + TagOpen "code" _ -> B.code <$> charsInTags "code" + TagOpen "tt" _ -> B.code <$> charsInTags "tt" + TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> charsInTags "hask" + _ -> B.rawInline "html" . snd <$> htmlTag (~== tag) + +special :: MWParser Inlines +special = B.str <$> count 1 (notFollowedBy' (htmlTag isBlockTag') *> + oneOf specialChars) + +inlineHtml :: MWParser Inlines +inlineHtml = B.rawInline "html" . snd <$> htmlTag isInlineTag' + +whitespace :: MWParser Inlines +whitespace = B.space <$ (skipMany1 spaceChar <|> endline <|> htmlComment) + +endline :: MWParser () +endline = () <$ try (newline <* + notFollowedBy blankline <* + notFollowedBy' hrule <* + notFollowedBy tableStart <* + notFollowedBy' header <* + notFollowedBy anyListStart) + +image :: MWParser Inlines +image = try $ do + sym "[[" + sym "File:" + fname <- many1 (noneOf "|]") + _ <- many (try $ char '|' *> imageOption) + caption <- (B.str fname <$ sym "]]") + <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]"))) + return $ B.image fname "image" caption + +imageOption :: MWParser String +imageOption = + try (oneOfStrings [ "border", "thumbnail", "frameless" + , "thumb", "upright", "left", "right" + , "center", "none", "baseline", "sub" + , "super", "top", "text-top", "middle" + , "bottom", "text-bottom" ]) + <|> try (string "frame") + <|> try (many1 (oneOf "x0123456789") <* string "px") + <|> try (oneOfStrings ["link=","alt=","page=","class="] <* many (noneOf "|]")) + +internalLink :: MWParser Inlines +internalLink = try $ do + sym "[[" + let addUnderscores x = let (pref,suff) = break (=='#') x + in pref ++ intercalate "_" (words suff) + pagename <- unwords . words <$> many (noneOf "|]") + label <- option (B.text pagename) $ char '|' *> + ( (mconcat <$> many1 (notFollowedBy (char ']') *> inline)) + -- the "pipe trick" + -- [[Help:Contents|] -> "Contents" + <|> (return $ B.text $ drop 1 $ dropWhile (/=':') pagename) ) + sym "]]" + linktrail <- B.text <$> many letter + let link = B.link (addUnderscores pagename) "wikilink" (label <> linktrail) + if "Category:" `isPrefixOf` pagename + then do + updateState $ \st -> st{ mwCategoryLinks = link : mwCategoryLinks st } + return mempty + else return link + +externalLink :: MWParser 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 $ show num + return $ B.link src "" lab + +url :: MWParser Inlines +url = do + (orig, src) <- uri + return $ B.link src "" (B.str orig) + +-- | Parses a list of inlines between start and end delimiters. +inlinesBetween :: (Show b) => MWParser a -> MWParser b -> MWParser Inlines +inlinesBetween start end = + (trimInlines . mconcat) <$> try (start >> many1Till inner end) + where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline) + innerSpace = try $ whitespace >>~ notFollowedBy' end + +emph :: MWParser Inlines +emph = B.emph <$> nested (inlinesBetween start end) + where start = sym "''" >> lookAhead nonspaceChar + end = try $ notFollowedBy' (() <$ strong) >> sym "''" + +strong :: MWParser Inlines +strong = B.strong <$> nested (inlinesBetween start end) + where start = sym "'''" >> lookAhead nonspaceChar + end = try $ sym "'''" + +doubleQuotes :: MWParser Inlines +doubleQuotes = B.doubleQuoted . trimInlines . mconcat <$> try + ((getState >>= guard . readerSmart . mwOptions) *> + openDoubleQuote *> manyTill inline closeDoubleQuote ) + where openDoubleQuote = char '"' <* lookAhead alphaNum + closeDoubleQuote = char '"' <* notFollowedBy alphaNum + diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 2c6fcc6e6..a0e5a0635 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Native Copyright : Copyright (C) 2011 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -31,6 +31,7 @@ Conversion of a string representation of a pandoc type (@Pandoc@, module Text.Pandoc.Readers.Native ( readNative ) where import Text.Pandoc.Definition +import Text.Pandoc.Shared (safeRead) nullMeta :: Meta nullMeta = Meta{ docTitle = [] @@ -51,31 +52,31 @@ nullMeta = Meta{ docTitle = [] readNative :: String -- ^ String to parse (assuming @'\n'@ line endings) -> Pandoc readNative s = - case reads s of - (d,_):_ -> d - [] -> Pandoc nullMeta $ readBlocks s + case safeRead s of + Just d -> d + Nothing -> Pandoc nullMeta $ readBlocks s readBlocks :: String -> [Block] readBlocks s = - case reads s of - (d,_):_ -> d - [] -> [readBlock s] + case safeRead s of + Just d -> d + Nothing -> [readBlock s] readBlock :: String -> Block readBlock s = - case reads s of - (d,_):_ -> d - [] -> Plain $ readInlines s + case safeRead s of + Just d -> d + Nothing -> Plain $ readInlines s readInlines :: String -> [Inline] readInlines s = - case reads s of - (d,_):_ -> d - [] -> [readInline s] + case safeRead s of + Just d -> d + Nothing -> [readInline s] readInline :: String -> Inline readInline s = - case reads s of - (d,_):_ -> d - [] -> error "Cannot parse document" + case safeRead s of + Just d -> d + Nothing -> error "Cannot parse document" diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index d1010a736..9699cf333 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -17,9 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.RST + Module : Text.Pandoc.Readers.RST Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -27,24 +27,33 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion from reStructuredText to 'Pandoc' document. -} -module Text.Pandoc.Readers.RST ( +module Text.Pandoc.Readers.RST ( readRST ) where import Text.Pandoc.Definition import Text.Pandoc.Shared import Text.Pandoc.Parsing -import Text.ParserCombinators.Parsec -import Control.Monad ( when, liftM ) -import Data.List ( findIndex, intercalate, transpose, sort, deleteFirstsBy ) +import Text.Pandoc.Options +import Control.Monad ( when, liftM, guard, mzero ) +import Data.List ( findIndex, intersperse, intercalate, + transpose, sort, deleteFirstsBy, isSuffixOf ) import qualified Data.Map as M import Text.Printf ( printf ) import Data.Maybe ( catMaybes ) +import Control.Applicative ((<$>), (<$), (<*), (*>)) +import Text.Pandoc.Builder (Inlines, Blocks, trimInlines, (<>)) +import qualified Text.Pandoc.Builder as B +import Data.Monoid (mconcat, mempty) +import Data.Sequence (viewr, ViewR(..)) +import Data.Char (toLower) -- | Parse reStructuredText string and return Pandoc document. -readRST :: ParserState -- ^ Parser state, including options for parser - -> String -- ^ String to parse (assuming @'\n'@ line endings) +readRST :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) -> Pandoc -readRST state s = (readWith parseRST) state (s ++ "\n\n") +readRST opts s = (readWith parseRST) def{ stateOptions = opts } (s ++ "\n\n") + +type RSTParser = Parser [Char] ParserState -- -- Constants and data structure definitions @@ -58,80 +67,75 @@ underlineChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" -- treat these as potentially non-text when parsing inline: specialChars :: [Char] -specialChars = "\\`|*_<>$:[]()-.\"'\8216\8217\8220\8221" +specialChars = "\\`|*_<>$:/[]{}()-.\"'\8216\8217\8220\8221" -- -- parsing documents -- isHeader :: Int -> Block -> Bool -isHeader n (Header x _) = x == n -isHeader _ _ = False +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 text):rest) = - (Header (level - num) text):(promoteHeaders num rest) +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. +-- promote all the other headers. titleTransform :: [Block] -- ^ list of blocks -> ([Block], [Inline]) -- ^ modified list of blocks, title -titleTransform ((Header 1 head1):(Header 2 head2):rest) | +titleTransform ((Header 1 _ head1):(Header 2 _ head2):rest) | not (any (isHeader 1) rest || any (isHeader 2) rest) = -- both title & subtitle (promoteHeaders 2 rest, head1 ++ [Str ":", Space] ++ head2) -titleTransform ((Header 1 head1):rest) | +titleTransform ((Header 1 _ head1):rest) | not (any (isHeader 1) rest) = -- title, no subtitle (promoteHeaders 1 rest, head1) titleTransform blocks = (blocks, []) -parseRST :: GenParser Char ParserState Pandoc +parseRST :: RSTParser Pandoc parseRST = do optional blanklines -- skip blank lines at beginning of file startPos <- getPosition -- go through once just to get list of reference keys and notes -- docMinusKeys is the raw document with blanks where the keys were... - docMinusKeys <- manyTill (referenceKey <|> noteBlock <|> lineClump) eof >>= - return . concat + docMinusKeys <- concat <$> + manyTill (referenceKey <|> noteBlock <|> lineClump) eof setInput docMinusKeys setPosition startPos st' <- getState let reversedNotes = stateNotes st' updateState $ \s -> s { stateNotes = reverse reversedNotes } -- now parse it for real... - blocks <- parseBlocks - let blocks' = filter (/= Null) blocks + blocks <- B.toList <$> parseBlocks + standalone <- getOption readerStandalone + let (blocks', title) = if standalone + then titleTransform blocks + else (blocks, []) state <- getState - let (blocks'', title) = if stateStandalone state - then titleTransform blocks' - else (blocks', []) let authors = stateAuthors state let date = stateDate state - let title' = if (null title) then (stateTitle state) else title - return $ Pandoc (Meta title' authors date) blocks'' + let title' = if null title then stateTitle state else title + return $ Pandoc (Meta title' authors date) blocks' -- -- parsing blocks -- -parseBlocks :: GenParser Char ParserState [Block] -parseBlocks = manyTill block eof +parseBlocks :: RSTParser Blocks +parseBlocks = mconcat <$> manyTill block eof -block :: GenParser Char ParserState Block +block :: RSTParser Blocks block = choice [ codeBlock - , rawBlock , blockQuote , fieldList - , imageBlock - , figureBlock - , customCodeBlock - , mathBlock - , defaultRoleBlock - , unknownDirective + , directive + , comment , header , hrule , lineBlock -- must go before definitionList @@ -139,34 +143,32 @@ block = choice [ codeBlock , list , lhsCodeBlock , para - , plain - , nullBlock ] <?> "block" + ] <?> "block" -- -- field list -- -rawFieldListItem :: String -> GenParser Char ParserState (String, String) +rawFieldListItem :: String -> RSTParser (String, String) rawFieldListItem indent = try $ do string indent char ':' - name <- many1 $ alphaNum <|> spaceChar - string ": " - skipSpaces + name <- many1Till (noneOf "\n") (char ':') + (() <$ lookAhead newline) <|> skipMany1 spaceChar first <- manyTill anyChar newline rest <- option "" $ try $ do lookAhead (string indent >> spaceChar) indentedBlock - let raw = first ++ "\n" ++ rest ++ "\n" + let raw = (if null first then "" else (first ++ "\n")) ++ rest ++ "\n" return (name, raw) fieldListItem :: String - -> GenParser Char ParserState (Maybe ([Inline], [[Block]])) + -> RSTParser (Maybe (Inlines, [Blocks])) fieldListItem indent = try $ do (name, raw) <- rawFieldListItem indent - let term = [Str name] - contents <- parseFromString (many block) raw + let term = B.str name + contents <- parseFromString parseBlocks raw optional blanklines - case (name, contents) of + case (name, B.toList contents) of ("Author", x) -> do updateState $ \st -> st{ stateAuthors = stateAuthors st ++ [extractContents x] } @@ -187,103 +189,69 @@ extractContents [Plain auth] = auth extractContents [Para auth] = auth extractContents _ = [] -fieldList :: GenParser Char ParserState Block +fieldList :: RSTParser Blocks fieldList = try $ do indent <- lookAhead $ many spaceChar items <- many1 $ fieldListItem indent - if null items - then return Null - else return $ DefinitionList $ catMaybes items + case catMaybes items of + [] -> return mempty + items' -> return $ B.definitionList items' -- -- line block -- -lineBlockLine :: GenParser Char ParserState [Inline] -lineBlockLine = try $ do - char '|' - char ' ' <|> lookAhead (char '\n') - white <- many spaceChar - line <- many $ (notFollowedBy newline >> inline) <|> (try $ endline >>~ char ' ') - optional endline - return $ if null white - then normalizeSpaces line - else Str white : normalizeSpaces line - -lineBlock :: GenParser Char ParserState Block +lineBlock :: RSTParser Blocks lineBlock = try $ do - lines' <- many1 lineBlockLine - blanklines - return $ Para (intercalate [LineBreak] lines') + lines' <- lineBlockLines + lines'' <- mapM (parseFromString + (trimInlines . mconcat <$> many inline)) lines' + return $ B.para (mconcat $ intersperse B.linebreak lines'') -- -- paragraph block -- -para :: GenParser Char ParserState Block -para = paraBeforeCodeBlock <|> paraNormal <?> "paragraph" - -codeBlockStart :: GenParser Char st Char -codeBlockStart = string "::" >> blankline >> blankline +-- note: paragraph can end in a :: starting a code block +para :: RSTParser 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) | "::" `isSuffixOf` xs -> do + raw <- option mempty codeBlockBody + return $ B.para (B.Many ys <> B.str (take (length xs - 1) xs)) + <> raw + _ -> return (B.para result) + +plain :: RSTParser Blocks +plain = B.plain . trimInlines . mconcat <$> many1 inline --- paragraph that ends in a :: starting a code block -paraBeforeCodeBlock :: GenParser Char ParserState Block -paraBeforeCodeBlock = try $ do - result <- many1 (notFollowedBy' codeBlockStart >> inline) - lookAhead (string "::") - return $ Para $ if last result == Space - then normalizeSpaces result - else (normalizeSpaces result) ++ [Str ":"] - --- regular paragraph -paraNormal :: GenParser Char ParserState Block -paraNormal = try $ do - result <- many1 inline - newline - blanklines - return $ Para $ normalizeSpaces result - -plain :: GenParser Char ParserState Block -plain = many1 inline >>= return . Plain . normalizeSpaces - --- --- image block --- - -imageBlock :: GenParser Char ParserState Block -imageBlock = try $ do - string ".. image:: " - src <- manyTill anyChar newline - fields <- try $ do indent <- lookAhead $ many (oneOf " /t") - many $ rawFieldListItem indent - optional blanklines - case lookup "alt" fields of - Just alt -> return $ Plain [Image [Str $ removeTrailingSpace alt] - (src, "")] - Nothing -> return $ Plain [Image [Str "image"] (src, "")] -- -- header blocks -- -header :: GenParser Char ParserState Block +header :: RSTParser Blocks header = doubleHeader <|> singleHeader <?> "header" -- a header with lines on top and bottom -doubleHeader :: GenParser Char ParserState Block +doubleHeader :: RSTParser Blocks doubleHeader = try $ do c <- oneOf underlineChars rest <- many (char c) -- the top line let lenTop = length (c:rest) skipSpaces newline - txt <- many1 (notFollowedBy blankline >> inline) + txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline) pos <- getPosition let len = (sourceColumn pos) - 1 if (len > lenTop) then fail "title longer than border" else return () blankline -- spaces and newline count lenTop (char c) -- the bottom line blanklines - -- check to see if we've had this kind of header before. + -- 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 @@ -291,13 +259,13 @@ doubleHeader = try $ do Just ind -> (headerTable, ind + 1) Nothing -> (headerTable ++ [DoubleHeader c], (length headerTable) + 1) setState (state { stateHeaderTable = headerTable' }) - return $ Header level (normalizeSpaces txt) + return $ B.header level txt -- a header with line on the bottom only -singleHeader :: GenParser Char ParserState Block -singleHeader = try $ do +singleHeader :: RSTParser Blocks +singleHeader = try $ do notFollowedBy' whitespace - txt <- many1 (do {notFollowedBy blankline; inline}) + txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy blankline; inline}) pos <- getPosition let len = (sourceColumn pos) - 1 blankline @@ -311,34 +279,34 @@ singleHeader = try $ do Just ind -> (headerTable, ind + 1) Nothing -> (headerTable ++ [SingleHeader c], (length headerTable) + 1) setState (state { stateHeaderTable = headerTable' }) - return $ Header level (normalizeSpaces txt) + return $ B.header level txt -- -- hrule block -- -hrule :: GenParser Char st Block +hrule :: Parser [Char] st Blocks hrule = try $ do chr <- oneOf underlineChars count 3 (char chr) skipMany (char chr) blankline blanklines - return HorizontalRule + return B.horizontalRule -- -- code blocks -- -- read a line indented by a given string -indentedLine :: String -> GenParser Char st [Char] +indentedLine :: String -> Parser [Char] st [Char] indentedLine indents = try $ do string indents manyTill anyChar newline -- one or more indented lines, possibly separated by blank lines. -- any amount of indentation will work. -indentedBlock :: GenParser Char st [Char] +indentedBlock :: Parser [Char] st [Char] indentedBlock = try $ do indents <- lookAhead $ many1 spaceChar lns <- many1 $ try $ do b <- option "" blanklines @@ -347,127 +315,65 @@ indentedBlock = try $ do optional blanklines return $ unlines lns -codeBlock :: GenParser Char st Block -codeBlock = try $ do - codeBlockStart - result <- indentedBlock - return $ CodeBlock ("",[],[]) $ stripTrailingNewlines result - --- | The 'code-block' directive (from Sphinx) that allows a language to be --- specified. -customCodeBlock :: GenParser Char st Block -customCodeBlock = try $ do - string ".. code-block:: " - language <- manyTill anyChar newline - blanklines - result <- indentedBlock - return $ CodeBlock ("", ["sourceCode", language], []) $ stripTrailingNewlines result - - -figureBlock :: GenParser Char ParserState Block -figureBlock = try $ do - string ".. figure::" - src <- removeLeadingTrailingSpace `fmap` manyTill anyChar newline - body <- indentedBlock - caption <- parseFromString extractCaption body - return $ Para [Image caption (src,"")] - -extractCaption :: GenParser Char ParserState [Inline] -extractCaption = try $ do - manyTill anyLine blanklines - many inline - --- | The 'math' directive (from Sphinx) for display math. -mathBlock :: GenParser Char st Block -mathBlock = try $ do - string ".. math::" - mathBlockMultiline <|> mathBlockOneLine - -mathBlockOneLine :: GenParser Char st Block -mathBlockOneLine = try $ do - result <- manyTill anyChar newline - blanklines - return $ Para [Math DisplayMath $ removeLeadingTrailingSpace result] +codeBlockStart :: Parser [Char] st Char +codeBlockStart = string "::" >> blankline >> blankline -mathBlockMultiline :: GenParser Char st Block -mathBlockMultiline = try $ do - blanklines - result <- indentedBlock - -- a single block can contain multiple equations, which need to go - -- in separate Pandoc math elements - let lns = map removeLeadingTrailingSpace $ lines result - -- drop :label, :nowrap, etc. - let startsWithColon (':':_) = True - startsWithColon _ = False - let lns' = dropWhile startsWithColon lns - let eqs = map (removeLeadingTrailingSpace . unlines) - $ filter (not . null) $ splitBy null lns' - return $ Para $ map (Math DisplayMath) eqs - -lhsCodeBlock :: GenParser Char ParserState Block +codeBlock :: Parser [Char] st Blocks +codeBlock = try $ codeBlockStart >> codeBlockBody + +codeBlockBody :: Parser [Char] st Blocks +codeBlockBody = try $ B.codeBlock . stripTrailingNewlines <$> indentedBlock + +lhsCodeBlock :: RSTParser Blocks lhsCodeBlock = try $ do - failUnlessLHS + getPosition >>= guard . (==1) . sourceColumn + guardEnabled Ext_literate_haskell optional codeBlockStart - pos <- getPosition - when (sourceColumn pos /= 1) $ fail "Not in first column" lns <- many1 birdTrackLine -- if (as is normal) there is always a space after >, drop it let lns' = if all (\ln -> null ln || take 1 ln == " ") lns then map (drop 1) lns else lns blanklines - return $ CodeBlock ("", ["sourceCode", "literate", "haskell"], []) $ intercalate "\n" lns' + return $ B.codeBlockWith ("", ["sourceCode", "literate", "haskell"], []) + $ intercalate "\n" lns' -birdTrackLine :: GenParser Char st [Char] -birdTrackLine = do - char '>' - manyTill anyChar newline - --- --- raw html/latex/etc --- - -rawBlock :: GenParser Char st Block -rawBlock = try $ do - string ".. raw:: " - lang <- many1 (letter <|> digit) - blanklines - result <- indentedBlock - return $ RawBlock lang result +birdTrackLine :: Parser [Char] st [Char] +birdTrackLine = char '>' >> manyTill anyChar newline -- -- block quotes -- -blockQuote :: GenParser Char ParserState Block +blockQuote :: RSTParser Blocks blockQuote = do raw <- indentedBlock -- parse the extracted block, which may contain various block elements: contents <- parseFromString parseBlocks $ raw ++ "\n\n" - return $ BlockQuote contents + return $ B.blockQuote contents -- -- list blocks -- -list :: GenParser Char ParserState Block +list :: RSTParser Blocks list = choice [ bulletList, orderedList, definitionList ] <?> "list" -definitionListItem :: GenParser Char ParserState ([Inline], [[Block]]) +definitionListItem :: RSTParser (Inlines, [Blocks]) definitionListItem = try $ do -- avoid capturing a directive or comment notFollowedBy (try $ char '.' >> char '.') - term <- many1Till inline endline + term <- trimInlines . mconcat <$> many1Till inline endline raw <- indentedBlock -- parse the extracted block, which may contain various block elements: contents <- parseFromString parseBlocks $ raw ++ "\n" - return (normalizeSpaces term, [contents]) + return (term, [contents]) -definitionList :: GenParser Char ParserState Block -definitionList = many1 definitionListItem >>= return . DefinitionList +definitionList :: RSTParser Blocks +definitionList = B.definitionList <$> many1 definitionListItem -- parses bullet list start and returns its length (inc. following whitespace) -bulletListStart :: GenParser Char st Int +bulletListStart :: Parser [Char] st Int bulletListStart = try $ do notFollowedBy' hrule -- because hrules start out just like lists marker <- oneOf bulletListMarkers @@ -477,14 +383,14 @@ bulletListStart = try $ do -- parses ordered list start and returns its length (inc following whitespace) orderedListStart :: ListNumberStyle -> ListNumberDelim - -> GenParser Char ParserState Int + -> RSTParser Int orderedListStart style delim = try $ do (_, markerLen) <- withHorizDisplacement (orderedListMarker style delim) white <- many1 spaceChar return $ markerLen + length white -- parse a line of a list item -listLine :: Int -> GenParser Char ParserState [Char] +listLine :: Int -> RSTParser [Char] listLine markerLength = try $ do notFollowedBy blankline indentWith markerLength @@ -492,36 +398,35 @@ listLine markerLength = try $ do return $ line ++ "\n" -- indent by specified number of spaces (or equiv. tabs) -indentWith :: Int -> GenParser Char ParserState [Char] +indentWith :: Int -> RSTParser [Char] indentWith num = do - state <- getState - let tabStop = stateTabStop state + tabStop <- getOption readerTabStop if (num < tabStop) then count num (char ' ') - else choice [ try (count num (char ' ')), - (try (char '\t' >> count (num - tabStop) (char ' '))) ] + else choice [ try (count num (char ' ')), + (try (char '\t' >> count (num - tabStop) (char ' '))) ] -- parse raw text for one list item, excluding start marker and continuations -rawListItem :: GenParser Char ParserState Int - -> GenParser Char ParserState (Int, [Char]) +rawListItem :: RSTParser Int + -> RSTParser (Int, [Char]) rawListItem start = try $ do markerLength <- start firstLine <- manyTill anyChar newline restLines <- many (listLine markerLength) return (markerLength, (firstLine ++ "\n" ++ (concat restLines))) --- continuation of a list item - indented and separated by blankline or --- (in compact lists) endline. +-- continuation of a list item - indented and separated by blankline or +-- (in compact lists) endline. -- Note: nested lists are parsed as continuations. -listContinuation :: Int -> GenParser Char ParserState [Char] +listContinuation :: Int -> RSTParser [Char] listContinuation markerLength = try $ do blanks <- many1 blankline result <- many1 (listLine markerLength) return $ blanks ++ concat result -listItem :: GenParser Char ParserState Int - -> GenParser Char ParserState [Block] -listItem start = try $ do +listItem :: RSTParser Int + -> RSTParser Blocks +listItem start = try $ do (markerLength, first) <- rawListItem start rest <- many (listContinuation markerLength) blanks <- choice [ try (many blankline >>~ lookAhead start), @@ -537,52 +442,181 @@ listItem start = try $ do updateState (\st -> st {stateParserContext = oldContext}) return parsed -orderedList :: GenParser Char ParserState Block +orderedList :: RSTParser Blocks orderedList = try $ do (start, style, delim) <- lookAhead (anyOrderedListMarker >>~ spaceChar) items <- many1 (listItem (orderedListStart style delim)) - let items' = compactify items - return $ OrderedList (start, style, delim) items' + let items' = compactify' items + return $ B.orderedListWith (start, style, delim) items' -bulletList :: GenParser Char ParserState Block -bulletList = many1 (listItem bulletListStart) >>= - return . BulletList . compactify +bulletList :: RSTParser Blocks +bulletList = B.bulletList . compactify' <$> many1 (listItem bulletListStart) -- --- default-role block +-- directive (e.g. comment, container, compound-paragraph) -- -defaultRoleBlock :: GenParser Char ParserState Block -defaultRoleBlock = try $ do - string ".. default-role::" - -- doesn't enforce any restrictions on the role name; embedded spaces shouldn't be allowed, for one - role <- manyTill anyChar newline >>= return . removeLeadingTrailingSpace - updateState $ \s -> s { stateRstDefaultRole = - if null role - then stateRstDefaultRole defaultParserState - else role - } - -- skip body of the directive if it exists - many $ blanklines <|> (spaceChar >> manyTill anyChar newline) - return Null - --- --- unknown directive (e.g. comment) --- - -unknownDirective :: GenParser Char st Block -unknownDirective = try $ do +comment :: RSTParser Blocks +comment = try $ do string ".." - notFollowedBy (noneOf " \t\n") - manyTill anyChar newline - many $ blanklines <|> (spaceChar >> manyTill anyChar newline) - return Null + skipMany1 spaceChar <|> (() <$ lookAhead newline) + notFollowedBy' directiveLabel + manyTill anyChar blanklines + optional indentedBlock + return mempty + +directiveLabel :: RSTParser String +directiveLabel = map toLower + <$> many1Till (letter <|> char '-') (try $ string "::") + +directive :: RSTParser Blocks +directive = try $ do + string ".." + directive' + +-- TODO: line-block, parsed-literal, table, csv-table, list-table +-- date +-- include +-- class +-- title +directive' :: RSTParser Blocks +directive' = do + skipMany1 spaceChar + label <- directiveLabel + skipMany spaceChar + top <- many $ satisfy (/='\n') + <|> try (char '\n' <* + notFollowedBy' (rawFieldListItem " ") <* + count 3 (char ' ') <* + notFollowedBy blankline) + newline + fields <- many $ rawFieldListItem " " + body <- option "" $ try $ blanklines >> indentedBlock + optional blanklines + let body' = body ++ "\n\n" + case label of + "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) + "role" -> return mempty + "container" -> parseFromString parseBlocks body' + "replace" -> B.para <$> -- consumed by substKey + parseFromString (trimInlines . mconcat <$> many inline) + (trim top) + "unicode" -> B.para <$> -- consumed by substKey + parseFromString (trimInlines . mconcat <$> many inline) + (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 <$> parseFromString + (trimInlines . mconcat <$> many inline) top + _ | label `elem` ["attention","caution","danger","error","hint", + "important","note","tip","warning"] -> + do let tit = B.para $ B.strong $ B.str label + bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + return $ B.blockQuote $ tit <> bod + "admonition" -> + do tit <- B.para . B.strong <$> parseFromString + (trimInlines . mconcat <$> many inline) top + bod <- parseFromString parseBlocks body' + return $ B.blockQuote $ tit <> bod + "sidebar" -> + do let subtit = maybe "" trim $ lookup "subtitle" fields + tit <- B.para . B.strong <$> parseFromString + (trimInlines . mconcat <$> many inline) + (trim top ++ if null subtit + then "" + else (": " ++ subtit)) + bod <- parseFromString parseBlocks body' + return $ B.blockQuote $ tit <> bod + "topic" -> + do tit <- B.para . B.strong <$> parseFromString + (trimInlines . mconcat <$> many inline) top + bod <- parseFromString parseBlocks body' + return $ tit <> bod + "default-role" -> mempty <$ updateState (\s -> + s { stateRstDefaultRole = + case trim top of + "" -> stateRstDefaultRole def + role -> role }) + "code" -> codeblock (lookup "number-lines" fields) (trim top) body + "code-block" -> codeblock (lookup "number-lines" fields) (trim top) body + "math" -> return $ B.para $ mconcat $ map B.displayMath + $ toChunks $ top ++ "\n\n" ++ body + "figure" -> do + (caption, legend) <- parseFromString extractCaption body' + let src = escapeURI $ trim top + return $ B.para (B.image src "" caption) <> legend + "image" -> do + let src = escapeURI $ trim top + let alt = B.str $ maybe "image" trim $ lookup "alt" fields + return $ B.para + $ case lookup "target" fields of + Just t -> B.link (escapeURI $ trim t) "" + $ B.image src "" alt + Nothing -> B.image src "" alt + _ -> return mempty + +-- Can contain haracter 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 :: String -> String +unicodeTransform t = + case t of + ('.':'.':xs) -> unicodeTransform $ dropWhile (/='\n') xs -- comment + ('0':'x':xs) -> go "0x" xs + ('x':xs) -> go "x" xs + ('\\':'x':xs) -> go "\\x" xs + ('U':'+':xs) -> go "U+" xs + ('u':xs) -> go "u" xs + ('\\':'u':xs) -> go "\\u" xs + ('&':'#':'x':xs) -> maybe ("&#x" ++ unicodeTransform xs) + -- drop semicolon + (\(c,s) -> c : unicodeTransform (drop 1 s)) + $ extractUnicodeChar xs + (x:xs) -> x : unicodeTransform xs + [] -> [] + where go pref zs = maybe (pref ++ unicodeTransform zs) + (\(c,s) -> c : unicodeTransform s) + $ extractUnicodeChar zs + +extractUnicodeChar :: String -> Maybe (Char, String) +extractUnicodeChar s = maybe Nothing (\c -> Just (c,rest)) mbc + where (ds,rest) = span isHexDigit s + mbc = safeRead ('\'':'\\':'x':ds ++ "'") + +isHexDigit :: Char -> Bool +isHexDigit c = c `elem` "0123456789ABCDEFabcdef" + +extractCaption :: RSTParser (Inlines, Blocks) +extractCaption = do + capt <- trimInlines . mconcat <$> many inline + legend <- optional blanklines >> (mconcat <$> many block) + return (capt,legend) + +-- divide string by blanklines +toChunks :: String -> [String] +toChunks = dropWhile null + . map (trim . unlines) + . splitBy (all (`elem` " \t")) . lines + +codeblock :: Maybe String -> String -> String -> RSTParser Blocks +codeblock numberLines lang body = + return $ B.codeBlockWith attribs $ stripTrailingNewlines body + where attribs = ("", classes, kvs) + classes = "sourceCode" : lang + : maybe [] (\_ -> ["numberLines"]) numberLines + kvs = case numberLines of + Just "" -> [] + Nothing -> [] + Just n -> [("startFrom",n)] --- --- note block --- -noteBlock :: GenParser Char ParserState [Char] +noteBlock :: RSTParser [Char] noteBlock = try $ do startPos <- getPosition string ".." @@ -601,7 +635,7 @@ noteBlock = try $ do -- return blanks so line count isn't affected return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -noteMarker :: GenParser Char ParserState [Char] +noteMarker :: RSTParser [Char] noteMarker = do char '[' res <- many1 digit @@ -614,82 +648,95 @@ noteMarker = do -- reference key -- -quotedReferenceName :: GenParser Char ParserState [Inline] +quotedReferenceName :: RSTParser Inlines quotedReferenceName = try $ do char '`' >> notFollowedBy (char '`') -- `` means inline code! - label' <- many1Till inline (char '`') + label' <- trimInlines . mconcat <$> many1Till inline (char '`') return label' -unquotedReferenceName :: GenParser Char ParserState [Inline] +unquotedReferenceName :: RSTParser Inlines unquotedReferenceName = try $ do - label' <- many1Till inline (lookAhead $ char ':') + label' <- trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':') return label' -- 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' :: GenParser Char st String +simpleReferenceName' :: Parser [Char] st String simpleReferenceName' = do x <- alphaNum xs <- many $ alphaNum <|> (try $ oneOf "-_:+." >> lookAhead alphaNum) return (x:xs) -simpleReferenceName :: GenParser Char st [Inline] +simpleReferenceName :: Parser [Char] st Inlines simpleReferenceName = do raw <- simpleReferenceName' - return [Str raw] + return $ B.str raw -referenceName :: GenParser Char ParserState [Inline] +referenceName :: RSTParser Inlines referenceName = quotedReferenceName <|> (try $ simpleReferenceName >>~ lookAhead (char ':')) <|> unquotedReferenceName -referenceKey :: GenParser Char ParserState [Char] +referenceKey :: RSTParser [Char] referenceKey = do startPos <- getPosition - (key, target) <- choice [imageKey, anonymousKey, regularKey] - st <- getState - let oldkeys = stateKeys st - updateState $ \s -> s { stateKeys = M.insert key target oldkeys } + choice [substKey, anonymousKey, regularKey] optional blanklines endPos <- getPosition -- return enough blanks to replace key return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -targetURI :: GenParser Char st [Char] +targetURI :: Parser [Char] st [Char] targetURI = do skipSpaces optional newline - contents <- many1 (try (many spaceChar >> newline >> + contents <- many1 (try (many spaceChar >> newline >> many1 spaceChar >> noneOf " \t\n") <|> noneOf "\n") blanklines - return $ escapeURI $ removeLeadingTrailingSpace $ contents + return $ escapeURI $ trim $ contents -imageKey :: GenParser Char ParserState (Key, Target) -imageKey = try $ do - string ".. |" - ref <- manyTill inline (char '|') - skipSpaces - string "image::" - src <- targetURI - return (toKey (normalizeSpaces ref), (src, "")) - -anonymousKey :: GenParser Char st (Key, Target) +substKey :: RSTParser () +substKey = try $ do + string ".." + skipMany1 spaceChar + (alt,ref) <- withRaw $ trimInlines . mconcat + <$> enclosed (char '|') (char '|') inline + res <- B.toList <$> directive' + il <- case res of + -- use alt unless :alt: attribute on image: + [Para [Image [Str "image"] (src,tit)]] -> + return $ B.image src tit alt + [Para [Link [Image [Str "image"] (src,tit)] (src',tit')]] -> + return $ B.link src' tit' (B.image src tit alt) + [Para ils] -> return $ B.fromList ils + _ -> mzero + let key = toKey $ stripFirstAndLast ref + updateState $ \s -> s{ stateSubstitutions = M.insert key il $ stateSubstitutions s } + +anonymousKey :: RSTParser () anonymousKey = try $ do oneOfStrings [".. __:", "__"] src <- targetURI pos <- getPosition - return (toKey [Str $ "_" ++ printf "%09d" (sourceLine pos)], (src, "")) + let key = toKey $ "_" ++ printf "%09d" (sourceLine pos) + updateState $ \s -> s { stateKeys = M.insert key (src,"") $ stateKeys s } + +stripTicks :: String -> String +stripTicks = reverse . stripTick . reverse . stripTick + where stripTick ('`':xs) = xs + stripTick xs = xs -regularKey :: GenParser Char ParserState (Key, Target) +regularKey :: RSTParser () regularKey = try $ do string ".. _" - ref <- referenceName + (_,ref) <- withRaw referenceName char ':' src <- targetURI - return (toKey (normalizeSpaces ref), (src, "")) + let key = toKey $ stripTicks ref + updateState $ \s -> s { stateKeys = M.insert key (src,"") $ stateKeys s } -- -- tables @@ -702,57 +749,57 @@ regularKey = try $ do -- Simple tables TODO: -- - column spans -- - multiline support --- - ensure that rightmost column span does not need to reach end +-- - ensure that rightmost column span does not need to reach end -- - require at least 2 columns -- -- Grid tables TODO: -- - column spans -dashedLine :: Char -> GenParser Char st (Int, Int) +dashedLine :: Char -> Parser [Char] st (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many (char ' ') return (length dashes, length $ dashes ++ sp) -simpleDashedLines :: Char -> GenParser Char st [(Int,Int)] +simpleDashedLines :: Char -> Parser [Char] st [(Int,Int)] simpleDashedLines ch = try $ many1 (dashedLine ch) -- Parse a table row separator -simpleTableSep :: Char -> GenParser Char ParserState Char +simpleTableSep :: Char -> RSTParser Char simpleTableSep ch = try $ simpleDashedLines ch >> newline -- Parse a table footer -simpleTableFooter :: GenParser Char ParserState [Char] +simpleTableFooter :: RSTParser [Char] simpleTableFooter = try $ simpleTableSep '=' >> blanklines -- Parse a raw line and split it into chunks by indices. -simpleTableRawLine :: [Int] -> GenParser Char ParserState [String] +simpleTableRawLine :: [Int] -> RSTParser [String] simpleTableRawLine indices = do line <- many1Till anyChar newline return (simpleTableSplitLine indices line) -- Parse a table row and return a list of blocks (columns). -simpleTableRow :: [Int] -> GenParser Char ParserState [[Block]] +simpleTableRow :: [Int] -> RSTParser [[Block]] simpleTableRow indices = do notFollowedBy' simpleTableFooter firstLine <- simpleTableRawLine indices colLines <- return [] -- TODO let cols = map unlines . transpose $ firstLine : colLines - mapM (parseFromString (many plain)) cols + mapM (parseFromString (B.toList . mconcat <$> many plain)) cols simpleTableSplitLine :: [Int] -> String -> [String] simpleTableSplitLine indices line = - map removeLeadingTrailingSpace + map trim $ tail $ splitByIndices (init indices) line -simpleTableHeader :: Bool -- ^ Headerless table - -> GenParser Char ParserState ([[Block]], [Alignment], [Int]) +simpleTableHeader :: Bool -- ^ Headerless table + -> RSTParser ([[Block]], [Alignment], [Int]) simpleTableHeader headless = try $ do optional blanklines rawContent <- if headless then return "" else simpleTableSep '=' >> anyLine - dashes <- simpleDashedLines '=' + dashes <- simpleDashedLines '=' <|> simpleDashedLines '-' newline let lines' = map snd dashes let indices = scanl (+) 0 lines' @@ -760,34 +807,34 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM (parseFromString (many plain)) $ - map removeLeadingTrailingSpace rawHeads + heads <- mapM (parseFromString (B.toList . mconcat <$> many plain)) $ + map trim rawHeads return (heads, aligns, indices) -- Parse a simple table. simpleTable :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block + -> RSTParser Blocks simpleTable headless = do - Table c a _w h l <- tableWith (simpleTableHeader headless) simpleTableRow sep simpleTableFooter (return []) + Table c a _w h l <- tableWith (simpleTableHeader headless) simpleTableRow sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) - return $ Table c a (replicate (length a) 0) h l + return $ B.singleton $ Table c a (replicate (length a) 0) h l where sep = return () -- optional (simpleTableSep '-') gridTable :: Bool -- ^ Headerless table - -> GenParser Char ParserState Block -gridTable = gridTableWith block (return []) + -> RSTParser Blocks +gridTable headerless = B.singleton + <$> gridTableWith (B.toList <$> parseBlocks) headerless -table :: GenParser Char ParserState Block +table :: RSTParser Blocks table = gridTable False <|> simpleTable False <|> gridTable True <|> simpleTable True <?> "table" +-- +-- inline +-- - -- - -- inline - -- - -inline :: GenParser Char ParserState Inline +inline :: RSTParser Inlines inline = choice [ whitespace , link , str @@ -795,91 +842,99 @@ inline = choice [ whitespace , strong , emph , code - , image - , superscript - , subscript - , math + , subst + , interpretedRole , note - , smartPunctuation inline + , smart , hyphens , escapedChar , symbol ] <?> "inline" -hyphens :: GenParser Char ParserState Inline +hyphens :: RSTParser Inlines hyphens = do result <- many1 (char '-') - option Space endline + optional endline -- don't want to treat endline after hyphen or dash as a space - return $ Str result + return $ B.str result -escapedChar :: GenParser Char st Inline +escapedChar :: Parser [Char] st Inlines escapedChar = do c <- escaped anyChar return $ if c == ' ' -- '\ ' is null in RST - then Str "" - else Str [c] + then mempty + else B.str [c] -symbol :: GenParser Char ParserState Inline -symbol = do +symbol :: RSTParser Inlines +symbol = do result <- oneOf specialChars - return $ Str [result] + return $ B.str [result] -- parses inline code, between codeStart and codeEnd -code :: GenParser Char ParserState Inline -code = try $ do +code :: RSTParser Inlines +code = try $ do string "``" result <- manyTill anyChar (try (string "``")) - return $ Code nullAttr - $ removeLeadingTrailingSpace $ intercalate " " $ lines result - -emph :: GenParser Char ParserState Inline -emph = enclosed (char '*') (char '*') inline >>= - return . Emph . normalizeSpaces - -strong :: GenParser Char ParserState Inline -strong = enclosed (string "**") (try $ string "**") inline >>= - return . Strong . normalizeSpaces - --- Parses inline interpreted text which is required to have the given role. --- This decision is based on the role marker (if present), --- and the current default interpreted text role. -interpreted :: [Char] -> GenParser Char ParserState [Char] -interpreted role = try $ do - state <- getState - if role == stateRstDefaultRole state - then try markedInterpretedText <|> unmarkedInterpretedText - else markedInterpretedText - where - markedInterpretedText = try (roleMarker >> unmarkedInterpretedText) - <|> (unmarkedInterpretedText >>= (\txt -> roleMarker >> return txt)) - roleMarker = string $ ":" ++ role ++ ":" - -- 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 - unmarkedInterpretedText = do - result <- enclosed (char '`') (char '`') anyChar - return result - -superscript :: GenParser Char ParserState Inline -superscript = interpreted "sup" >>= \x -> return (Superscript [Str x]) + return $ B.code + $ trim $ unwords $ lines result -subscript :: GenParser Char ParserState Inline -subscript = interpreted "sub" >>= \x -> return (Subscript [Str x]) - -math :: GenParser Char ParserState Inline -math = interpreted "math" >>= \x -> return (Math InlineMath x) - -whitespace :: GenParser Char ParserState Inline -whitespace = many1 spaceChar >> return Space <?> "whitespace" - -str :: GenParser Char ParserState Inline -str = do - result <- many1 (noneOf (specialChars ++ "\t\n ")) +-- succeeds only if we're not right after a str (ie. in middle of word) +atStart :: RSTParser a -> RSTParser a +atStart p = do pos <- getPosition - updateState $ \s -> s{ stateLastStrPos = Just pos } - return $ Str result + st <- getState + -- single quote start can't be right after str + guard $ stateLastStrPos st /= Just pos + p + +emph :: RSTParser Inlines +emph = B.emph . trimInlines . mconcat <$> + enclosed (atStart $ char '*') (char '*') inline + +strong :: RSTParser Inlines +strong = B.strong . trimInlines . mconcat <$> + enclosed (atStart $ string "**") (try $ string "**") inline + +-- 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 +interpretedRole :: RSTParser Inlines +interpretedRole = try $ do + (role, contents) <- roleBefore <|> roleAfter + case role of + "sup" -> return $ B.superscript $ B.str contents + "sub" -> return $ B.subscript $ B.str contents + "math" -> return $ B.math contents + _ -> return $ B.str contents --unknown + +roleMarker :: RSTParser String +roleMarker = char ':' *> many1Till (letter <|> char '-') (char ':') + +roleBefore :: RSTParser (String,String) +roleBefore = try $ do + role <- roleMarker + contents <- unmarkedInterpretedText + return (role,contents) + +roleAfter :: RSTParser (String,String) +roleAfter = try $ do + contents <- unmarkedInterpretedText + role <- roleMarker <|> (stateRstDefaultRole <$> getState) + return (role,contents) + +unmarkedInterpretedText :: RSTParser [Char] +unmarkedInterpretedText = enclosed (atStart $ char '`') (char '`') anyChar + +whitespace :: RSTParser Inlines +whitespace = B.space <$ skipMany1 spaceChar <?> "whitespace" + +str :: RSTParser Inlines +str = do + let strChar = noneOf ("\t\n " ++ specialChars) + result <- many1 strChar + updateLastStrPos + return $ B.str result -- an endline character that can be treated as a space, not a structural break -endline :: GenParser Char ParserState Inline +endline :: RSTParser Inlines endline = try $ do newline notFollowedBy blankline @@ -889,74 +944,70 @@ endline = try $ do then notFollowedBy (anyOrderedListMarker >> spaceChar) >> notFollowedBy' bulletListStart else return () - return Space + return B.space -- -- links -- -link :: GenParser Char ParserState Inline +link :: RSTParser Inlines link = choice [explicitLink, referenceLink, autoLink] <?> "link" -explicitLink :: GenParser Char ParserState Inline +explicitLink :: RSTParser Inlines explicitLink = try $ do char '`' notFollowedBy (char '`') -- `` marks start of inline code - label' <- manyTill (notFollowedBy (char '`') >> inline) - (try (spaces >> char '<')) + label' <- trimInlines . mconcat <$> + manyTill (notFollowedBy (char '`') >> inline) (char '<') src <- manyTill (noneOf ">\n") (char '>') skipSpaces string "`_" - return $ Link (normalizeSpaces label') - (escapeURI $ removeLeadingTrailingSpace src, "") + return $ B.link (escapeURI $ trim src) "" label' -referenceLink :: GenParser Char ParserState Inline +referenceLink :: RSTParser Inlines referenceLink = try $ do - label' <- (quotedReferenceName <|> simpleReferenceName) >>~ char '_' + (label',ref) <- withRaw (quotedReferenceName <|> simpleReferenceName) >>~ + char '_' state <- getState let keyTable = stateKeys state - let isAnonKey x = case fromKey x of - [Str ('_':_)] -> True - _ -> False - key <- option (toKey label') $ + let isAnonKey (Key ('_':_)) = True + isAnonKey _ = False + key <- option (toKey $ stripTicks ref) $ do char '_' let anonKeys = sort $ filter isAnonKey $ M.keys keyTable if null anonKeys - then pzero + then mzero else return (head anonKeys) - (src,tit) <- case lookupKeySrc keyTable key of + (src,tit) <- case M.lookup key keyTable of Nothing -> fail "no corresponding key" Just target -> return target -- if anonymous link, remove key so it won't be used again when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable } - return $ Link (normalizeSpaces label') (src, tit) + return $ B.link src tit label' -autoURI :: GenParser Char ParserState Inline +autoURI :: RSTParser Inlines autoURI = do (orig, src) <- uri - return $ Link [Str orig] (src, "") + return $ B.link src "" $ B.str orig -autoEmail :: GenParser Char ParserState Inline +autoEmail :: RSTParser Inlines autoEmail = do (orig, src) <- emailAddress - return $ Link [Str orig] (src, "") + return $ B.link src "" $ B.str orig -autoLink :: GenParser Char ParserState Inline +autoLink :: RSTParser Inlines autoLink = autoURI <|> autoEmail --- For now, we assume that all substitution references are for images. -image :: GenParser Char ParserState Inline -image = try $ do - char '|' - ref <- manyTill inline (char '|') +subst :: RSTParser Inlines +subst = try $ do + (_,ref) <- withRaw $ enclosed (char '|') (char '|') inline state <- getState - let keyTable = stateKeys state - (src,tit) <- case lookupKeySrc keyTable (toKey ref) of - Nothing -> fail "no corresponding key" - Just target -> return target - return $ Image (normalizeSpaces ref) (src, tit) + let substTable = stateSubstitutions state + case M.lookup (toKey $ stripFirstAndLast ref) substTable of + Nothing -> fail "no corresponding key" + Just target -> return target -note :: GenParser Char ParserState Inline +note :: RSTParser Inlines note = try $ do ref <- noteMarker char '_' @@ -977,4 +1028,24 @@ note = try $ do then deleteFirstsBy (==) notes [(ref,raw)] else notes updateState $ \st -> st{ stateNotes = newnotes } - return $ Note contents + return $ B.note contents + +smart :: RSTParser Inlines +smart = do + getOption readerSmart >>= guard + doubleQuoted <|> singleQuoted <|> + choice (map (B.singleton <$>) [apostrophe, dash, ellipses]) + +singleQuoted :: RSTParser Inlines +singleQuoted = try $ do + singleQuoteStart + withQuoteContext InSingleQuote $ + B.singleQuoted . trimInlines . mconcat <$> + many1Till inline singleQuoteEnd + +doubleQuoted :: RSTParser Inlines +doubleQuoted = try $ do + doubleQuoteStart + withQuoteContext InDoubleQuote $ + B.doubleQuoted . trimInlines . mconcat <$> + many1Till inline doubleQuoteEnd diff --git a/src/Text/Pandoc/Readers/TeXMath.hs b/src/Text/Pandoc/Readers/TeXMath.hs index 67dfe6753..fe49a992e 100644 --- a/src/Text/Pandoc/Readers/TeXMath.hs +++ b/src/Text/Pandoc/Readers/TeXMath.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.TeXMath Copyright : Copyright (C) 2007-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 348900d38..3ac7f4efb 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Textile Copyright : Copyright (C) 2010-2012 Paul Rivier and John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : Paul Rivier <paul*rivier#demotera*com> Stability : alpha @@ -46,8 +46,6 @@ Left to be implemented: - continued blocks (ex bq..) TODO : refactor common patterns across readers : - - autolink - - smartPunctuation - more ... -} @@ -56,29 +54,35 @@ TODO : refactor common patterns across readers : module Text.Pandoc.Readers.Textile ( readTextile) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML ( htmlTag, isInlineTag, isBlockTag ) import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXBlock ) -import Text.ParserCombinators.Parsec import Text.HTML.TagSoup.Match +import Data.List ( intercalate ) import Data.Char ( digitToInt, isUpper ) import Control.Monad ( guard, liftM ) import Control.Applicative ((<$>), (*>), (<*)) -- | Parse a Textile text and return a Pandoc document. -readTextile :: ParserState -- ^ Parser state, including options for parser - -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Pandoc -readTextile state s = - (readWith parseTextile) state{ stateOldDashes = True } (s ++ "\n\n") +readTextile :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Pandoc +readTextile opts s = + (readWith parseTextile) def{ stateOptions = opts } (s ++ "\n\n") -- | Generate a Pandoc ADT from a textile document -parseTextile :: GenParser Char ParserState Pandoc +parseTextile :: Parser [Char] ParserState Pandoc parseTextile = do -- textile allows raw HTML and does smart punctuation by default - updateState (\state -> state { stateParseRaw = True, stateSmart = True }) + oldOpts <- stateOptions `fmap` getState + updateState $ \state -> state{ stateOptions = + oldOpts{ readerSmart = True + , readerParseRaw = True + , readerOldDashes = True + } } many blankline startPos <- getPosition -- go through once just to get list of reference keys and notes @@ -93,10 +97,10 @@ parseTextile = do blocks <- parseBlocks return $ Pandoc (Meta [] [] []) blocks -- FIXME -noteMarker :: GenParser Char ParserState [Char] +noteMarker :: Parser [Char] ParserState [Char] noteMarker = skipMany spaceChar >> string "fn" >> manyTill digit (char '.') -noteBlock :: GenParser Char ParserState [Char] +noteBlock :: Parser [Char] ParserState [Char] noteBlock = try $ do startPos <- getPosition ref <- noteMarker @@ -111,37 +115,44 @@ noteBlock = try $ do return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -- | Parse document blocks -parseBlocks :: GenParser Char ParserState [Block] +parseBlocks :: Parser [Char] ParserState [Block] parseBlocks = manyTill block eof -- | Block parsers list tried in definition order -blockParsers :: [GenParser Char ParserState Block] +blockParsers :: [Parser [Char] ParserState Block] blockParsers = [ codeBlock , header , blockQuote , hrule + , commentBlock , anyList , rawHtmlBlock , rawLaTeXBlock' , maybeExplicitBlock "table" table , maybeExplicitBlock "p" para - , nullBlock ] + ] -- | Any block in the order of definition of blockParsers -block :: GenParser Char ParserState Block +block :: Parser [Char] ParserState Block block = choice blockParsers <?> "block" -codeBlock :: GenParser Char ParserState Block +commentBlock :: Parser [Char] ParserState Block +commentBlock = try $ do + string "###." + manyTill anyLine blanklines + return Null + +codeBlock :: Parser [Char] ParserState Block codeBlock = codeBlockBc <|> codeBlockPre -codeBlockBc :: GenParser Char ParserState Block +codeBlockBc :: Parser [Char] ParserState Block codeBlockBc = try $ do string "bc. " contents <- manyTill anyLine blanklines return $ CodeBlock ("",[],[]) $ unlines contents -- | Code Blocks in Textile are between <pre> and </pre> -codeBlockPre :: GenParser Char ParserState Block +codeBlockPre :: Parser [Char] ParserState Block codeBlockPre = try $ do htmlTag (tagOpen (=="pre") null) result' <- manyTill anyChar (try $ htmlTag (tagClose (=="pre")) >> blockBreak) @@ -156,23 +167,28 @@ codeBlockPre = try $ do return $ CodeBlock ("",[],[]) result''' -- | Header of the form "hN. content" with N in 1..6 -header :: GenParser Char ParserState Block +header :: Parser [Char] ParserState Block header = try $ do char 'h' level <- digitToInt <$> oneOf "123456" - optional attributes >> char '.' >> whitespace + attr <- option "" attributes + let ident = case attr of + '#':xs -> xs + _ -> "" + char '.' + whitespace name <- normalizeSpaces <$> manyTill inline blockBreak - return $ Header level name + return $ Header level (ident,[],[]) name -- | Blockquote of the form "bq. content" -blockQuote :: GenParser Char ParserState Block +blockQuote :: Parser [Char] ParserState Block blockQuote = try $ do string "bq" >> optional attributes >> char '.' >> whitespace BlockQuote . singleton <$> para -- Horizontal rule -hrule :: GenParser Char st Block +hrule :: Parser [Char] st Block hrule = try $ do skipSpaces start <- oneOf "-*" @@ -187,62 +203,72 @@ hrule = try $ do -- | 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 :: GenParser Char ParserState Block -anyList = try $ ( (anyListAtDepth 1) <* blanklines ) +anyList :: Parser [Char] ParserState Block +anyList = try $ anyListAtDepth 1 <* blanklines -- | This allow one type of list to be nested into an other type, -- provided correct nesting -anyListAtDepth :: Int -> GenParser Char ParserState Block +anyListAtDepth :: Int -> Parser [Char] ParserState Block anyListAtDepth depth = choice [ bulletListAtDepth depth, orderedListAtDepth depth, definitionList ] -- | Bullet List of given depth, depth being the number of leading '*' -bulletListAtDepth :: Int -> GenParser Char ParserState Block +bulletListAtDepth :: Int -> Parser [Char] ParserState Block bulletListAtDepth depth = try $ BulletList <$> many1 (bulletListItemAtDepth depth) -- | Bullet List Item of given depth, depth being the number of -- leading '*' -bulletListItemAtDepth :: Int -> GenParser Char ParserState [Block] +bulletListItemAtDepth :: Int -> Parser [Char] ParserState [Block] bulletListItemAtDepth = genericListItemAtDepth '*' -- | Ordered List of given depth, depth being the number of -- leading '#' -orderedListAtDepth :: Int -> GenParser Char ParserState Block +orderedListAtDepth :: Int -> Parser [Char] ParserState Block orderedListAtDepth depth = try $ do items <- many1 (orderedListItemAtDepth depth) return (OrderedList (1, DefaultStyle, DefaultDelim) items) -- | Ordered List Item of given depth, depth being the number of -- leading '#' -orderedListItemAtDepth :: Int -> GenParser Char ParserState [Block] +orderedListItemAtDepth :: Int -> Parser [Char] ParserState [Block] orderedListItemAtDepth = genericListItemAtDepth '#' -- | Common implementation of list items -genericListItemAtDepth :: Char -> Int -> GenParser Char ParserState [Block] +genericListItemAtDepth :: Char -> Int -> Parser [Char] ParserState [Block] genericListItemAtDepth c depth = try $ do count depth (char c) >> optional attributes >> whitespace - p <- inlines + p <- many listInline + newline sublist <- option [] (singleton <$> anyListAtDepth (depth + 1)) - return ((Plain p):sublist) + return (Plain p : sublist) -- | A definition list is a set of consecutive definition items -definitionList :: GenParser Char ParserState Block +definitionList :: Parser [Char] ParserState Block definitionList = try $ DefinitionList <$> many1 definitionListItem - + +-- | List start character. +listStart :: Parser [Char] st Char +listStart = oneOf "*#-" + +listInline :: Parser [Char] ParserState Inline +listInline = try (notFollowedBy newline >> inline) + <|> try (endline <* notFollowedBy listStart) + -- | 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 :: GenParser Char ParserState ([Inline], [[Block]]) +definitionListItem :: Parser [Char] ParserState ([Inline], [[Block]]) definitionListItem = try $ do string "- " term <- many1Till inline (try (whitespace >> string ":=")) - def <- inlineDef <|> multilineDef - return (term, def) - where inlineDef :: GenParser Char ParserState [[Block]] - inlineDef = liftM (\d -> [[Plain d]]) $ try (whitespace >> inlines) - multilineDef :: GenParser Char ParserState [[Block]] + def' <- multilineDef <|> inlineDef + return (term, def') + where inlineDef :: Parser [Char] ParserState [[Block]] + inlineDef = liftM (\d -> [[Plain d]]) + $ optional whitespace >> many listInline <* newline + multilineDef :: Parser [Char] ParserState [[Block]] multilineDef = try $ do optional whitespace >> newline s <- many1Till anyChar (try (string "=:" >> newline)) @@ -252,77 +278,78 @@ definitionListItem = try $ do -- | This terminates a block such as a paragraph. Because of raw html -- blocks support, we have to lookAhead for a rawHtmlBlock. -blockBreak :: GenParser Char ParserState () +blockBreak :: Parser [Char] ParserState () blockBreak = try (newline >> blanklines >> return ()) <|> (lookAhead rawHtmlBlock >> return ()) -- raw content -- | A raw Html Block, optionally followed by blanklines -rawHtmlBlock :: GenParser Char ParserState Block +rawHtmlBlock :: Parser [Char] ParserState Block rawHtmlBlock = try $ do (_,b) <- htmlTag isBlockTag optional blanklines return $ RawBlock "html" b -- | Raw block of LaTeX content -rawLaTeXBlock' :: GenParser Char ParserState Block +rawLaTeXBlock' :: Parser [Char] ParserState Block rawLaTeXBlock' = do - failIfStrict + guardEnabled Ext_raw_tex RawBlock "latex" <$> (rawLaTeXBlock <* spaces) -- | In textile, paragraphs are separated by blank lines. -para :: GenParser Char ParserState Block +para :: Parser [Char] ParserState Block para = try $ Para . normalizeSpaces <$> manyTill inline blockBreak -- Tables - + -- | A table cell spans until a pipe | -tableCell :: GenParser Char ParserState TableCell +tableCell :: Parser [Char] ParserState TableCell tableCell = do c <- many1 (noneOf "|\n") content <- parseFromString (many1 inline) c return $ [ Plain $ normalizeSpaces content ] -- | A table row is made of many table cells -tableRow :: GenParser Char ParserState [TableCell] -tableRow = try $ ( char '|' *> (endBy1 tableCell (char '|')) <* newline) +tableRow :: Parser [Char] ParserState [TableCell] +tableRow = try $ ( char '|' *> + (endBy1 tableCell (optional blankline *> char '|')) <* newline) -- | Many table rows -tableRows :: GenParser Char ParserState [[TableCell]] +tableRows :: Parser [Char] ParserState [[TableCell]] tableRows = many1 tableRow -- | Table headers are made of cells separated by a tag "|_." -tableHeaders :: GenParser Char ParserState [TableCell] +tableHeaders :: Parser [Char] ParserState [TableCell] tableHeaders = let separator = (try $ string "|_.") in try $ ( separator *> (sepBy1 tableCell separator) <* char '|' <* newline ) - + -- | A table with an optional header. Current implementation can -- handle tables with and without header, but will parse cells -- alignment attributes as content. -table :: GenParser Char ParserState Block +table :: Parser [Char] ParserState Block table = try $ do headers <- option [] tableHeaders rows <- tableRows blanklines let nbOfCols = max (length headers) (length $ head rows) - return $ Table [] + return $ Table [] (replicate nbOfCols AlignDefault) (replicate nbOfCols 0.0) headers rows - + -- | Blocks like 'p' and 'table' do not need explicit block tag. -- However, they can be used to set HTML/CSS attributes when needed. maybeExplicitBlock :: String -- ^ block tag name - -> GenParser Char ParserState Block -- ^ implicit block - -> GenParser Char ParserState Block + -> Parser [Char] ParserState Block -- ^ implicit block + -> Parser [Char] ParserState Block maybeExplicitBlock name blk = try $ do - optional $ try $ string name >> optional attributes >> char '.' >> - ((try whitespace) <|> endline) + optional $ try $ string name >> optional attributes >> char '.' >> + optional whitespace >> optional endline blk @@ -333,17 +360,12 @@ maybeExplicitBlock name blk = try $ do -- | Any inline element -inline :: GenParser Char ParserState Inline +inline :: Parser [Char] ParserState Inline inline = choice inlineParsers <?> "inline" --- | List of consecutive inlines before a newline -inlines :: GenParser Char ParserState [Inline] -inlines = manyTill inline newline - -- | Inline parsers tried in order -inlineParsers :: [GenParser Char ParserState Inline] -inlineParsers = [ autoLink - , str +inlineParsers :: [Parser [Char] ParserState Inline] +inlineParsers = [ str , whitespace , endline , code @@ -362,42 +384,42 @@ inlineParsers = [ autoLink ] -- | Inline markups -inlineMarkup :: GenParser Char ParserState Inline +inlineMarkup :: Parser [Char] ParserState Inline inlineMarkup = choice [ simpleInline (string "??") (Cite []) , simpleInline (string "**") Strong , simpleInline (string "__") Emph , simpleInline (char '*') Strong , simpleInline (char '_') Emph , simpleInline (char '+') Emph -- approximates underline - , simpleInline (char '-') Strikeout + , simpleInline (char '-' <* notFollowedBy (char '-')) Strikeout , simpleInline (char '^') Superscript , simpleInline (char '~') Subscript ] -- | Trademark, registered, copyright -mark :: GenParser Char st Inline +mark :: Parser [Char] st Inline mark = try $ char '(' >> (try tm <|> try reg <|> copy) -reg :: GenParser Char st Inline +reg :: Parser [Char] st Inline reg = do oneOf "Rr" char ')' return $ Str "\174" -tm :: GenParser Char st Inline +tm :: Parser [Char] st Inline tm = do oneOf "Tt" oneOf "Mm" char ')' return $ Str "\8482" -copy :: GenParser Char st Inline +copy :: Parser [Char] st Inline copy = do oneOf "Cc" char ')' return $ Str "\169" -note :: GenParser Char ParserState Inline +note :: Parser [Char] ParserState Inline note = try $ do ref <- (char '[' *> many1 digit <* char ']') notes <- stateNotes <$> getState @@ -405,9 +427,9 @@ note = try $ do Nothing -> fail "note not found" Just raw -> liftM Note $ parseFromString parseBlocks raw --- | Special chars +-- | Special chars markupChars :: [Char] -markupChars = "\\[]*#_@~-+^|%=" +markupChars = "\\*#_@~-+^|%=[]" -- | Break strings on following chars. Space tab and newline break for -- inlines breaking. Open paren breaks for mark. Quote, dash and dot @@ -415,23 +437,28 @@ markupChars = "\\[]*#_@~-+^|%=" -- punctuation. Double quote breaks for named links. > and < break -- for inline html. stringBreakers :: [Char] -stringBreakers = " \t\n('-.,:!?;\"<>" +stringBreakers = " \t\n\r.,\"'?!;:<>«»„“”‚‘’()[]" wordBoundaries :: [Char] wordBoundaries = markupChars ++ stringBreakers -- | Parse a hyphened sequence of words -hyphenedWords :: GenParser Char ParserState String -hyphenedWords = try $ do +hyphenedWords :: Parser [Char] ParserState String +hyphenedWords = do + x <- wordChunk + xs <- many (try $ char '-' >> wordChunk) + return $ intercalate "-" (x:xs) + +wordChunk :: Parser [Char] ParserState String +wordChunk = try $ do hd <- noneOf wordBoundaries - tl <- many ( (noneOf wordBoundaries) <|> - try (oneOf markupChars <* lookAhead (noneOf wordBoundaries) ) ) - let wd = hd:tl - option wd $ try $ - (\r -> concat [wd, "-", r]) <$> (char '-' *> hyphenedWords) + tl <- many ( (noneOf wordBoundaries) <|> + try (notFollowedBy' note *> oneOf markupChars + <* lookAhead (noneOf wordBoundaries) ) ) + return $ hd:tl -- | Any string -str :: GenParser Char ParserState Inline +str :: Parser [Char] ParserState Inline str = do baseStr <- hyphenedWords -- RedCloth compliance : if parsed word is uppercase and immediatly @@ -444,44 +471,53 @@ str = do return $ Str fullStr -- | Textile allows HTML span infos, we discard them -htmlSpan :: GenParser Char ParserState Inline +htmlSpan :: Parser [Char] ParserState Inline htmlSpan = try $ Str <$> ( char '%' *> attributes *> manyTill anyChar (char '%') ) -- | Some number of space chars -whitespace :: GenParser Char ParserState Inline +whitespace :: Parser [Char] ParserState Inline whitespace = many1 spaceChar >> return Space <?> "whitespace" -- | In Textile, an isolated endline character is a line break -endline :: GenParser Char ParserState Inline +endline :: Parser [Char] ParserState Inline endline = try $ do newline >> notFollowedBy blankline return LineBreak -rawHtmlInline :: GenParser Char ParserState Inline +rawHtmlInline :: Parser [Char] ParserState Inline rawHtmlInline = RawInline "html" . snd <$> htmlTag isInlineTag - --- | Raw LaTeX Inline -rawLaTeXInline' :: GenParser Char ParserState Inline + +-- | Raw LaTeX Inline +rawLaTeXInline' :: Parser [Char] ParserState Inline rawLaTeXInline' = try $ do - failIfStrict + guardEnabled Ext_raw_tex rawLaTeXInline --- | Textile standard link syntax is "label":target -link :: GenParser Char ParserState Inline -link = try $ do +-- | Textile standard link syntax is "label":target. But we +-- can also have ["label":target]. +link :: Parser [Char] ParserState Inline +link = linkB <|> linkNoB + +linkNoB :: Parser [Char] ParserState Inline +linkNoB = try $ do name <- surrounded (char '"') inline char ':' - url <- manyTill (anyChar) (lookAhead $ (space <|> try (oneOf ".;,:" >> (space <|> newline)))) - return $ Link name (url, "") - --- | Detect plain links to http or email. -autoLink :: GenParser Char ParserState Inline -autoLink = do - (orig, src) <- (try uri <|> try emailAddress) - return $ Link [Str orig] (src, "") + let stopChars = "!.,;:" + url <- manyTill nonspaceChar (lookAhead $ space <|> try (oneOf stopChars >> (space <|> newline))) + let name' = if name == [Str "$"] then [Str url] else name + return $ Link name' (url, "") + +linkB :: Parser [Char] ParserState Inline +linkB = try $ do + char '[' + name <- surrounded (char '"') inline + char ':' + url <- manyTill nonspaceChar (char ']') + let name' = if name == [Str "$"] then [Str url] else name + return $ Link name' (url, "") -- | image embedding -image :: GenParser Char ParserState Inline +image :: Parser [Char] ParserState Inline image = try $ do char '!' >> notFollowedBy space src <- manyTill anyChar (lookAhead $ oneOf "!(") @@ -489,49 +525,49 @@ image = try $ do char '!' return $ Image [Str alt] (src, alt) -escapedInline :: GenParser Char ParserState Inline +escapedInline :: Parser [Char] ParserState Inline escapedInline = escapedEqs <|> escapedTag -escapedEqs :: GenParser Char ParserState Inline +escapedEqs :: Parser [Char] ParserState Inline escapedEqs = Str <$> (try $ string "==" *> manyTill anyChar (try $ string "==")) -- | literal text escaped btw <notextile> tags -escapedTag :: GenParser Char ParserState Inline +escapedTag :: Parser [Char] ParserState Inline escapedTag = Str <$> (try $ string "<notextile>" *> manyTill anyChar (try $ string "</notextile>")) -- | Any special symbol defined in wordBoundaries -symbol :: GenParser Char ParserState Inline -symbol = Str . singleton <$> oneOf wordBoundaries +symbol :: Parser [Char] ParserState Inline +symbol = Str . singleton <$> (oneOf wordBoundaries <|> oneOf markupChars) -- | Inline code -code :: GenParser Char ParserState Inline +code :: Parser [Char] ParserState Inline code = code1 <|> code2 -code1 :: GenParser Char ParserState Inline +code1 :: Parser [Char] ParserState Inline code1 = Code nullAttr <$> surrounded (char '@') anyChar -code2 :: GenParser Char ParserState Inline +code2 :: Parser [Char] ParserState Inline code2 = do htmlTag (tagOpen (=="tt") null) Code nullAttr <$> manyTill anyChar (try $ htmlTag $ tagClose (=="tt")) -- | Html / CSS attributes -attributes :: GenParser Char ParserState String +attributes :: Parser [Char] ParserState String attributes = choice [ enclosed (char '(') (char ')') anyChar, enclosed (char '{') (char '}') anyChar, enclosed (char '[') (char ']') anyChar] -- | Parses material surrounded by a parser. -surrounded :: GenParser Char st t -- ^ surrounding parser - -> GenParser Char st a -- ^ content parser (to be used repeatedly) - -> GenParser Char st [a] -surrounded border = enclosed border (try border) +surrounded :: Parser [Char] st t -- ^ surrounding parser + -> Parser [Char] st a -- ^ content parser (to be used repeatedly) + -> Parser [Char] st [a] +surrounded border = enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border) -- | Inlines are most of the time of the same form -simpleInline :: GenParser Char ParserState t -- ^ surrounding parser +simpleInline :: Parser [Char] ParserState t -- ^ surrounding parser -> ([Inline] -> Inline) -- ^ Inline constructor - -> GenParser Char ParserState Inline -- ^ content parser (to be used repeatedly) + -> Parser [Char] ParserState Inline -- ^ content parser (to be used repeatedly) simpleInline border construct = surrounded border (inlineWithAttribute) >>= return . construct . normalizeSpaces where inlineWithAttribute = (try $ optional attributes) >> inline diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index a80ab0c63..780d2de33 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -32,52 +32,19 @@ the HTML using data URIs. -} module Text.Pandoc.SelfContained ( makeSelfContained ) where import Text.HTML.TagSoup -import Network.URI (isAbsoluteURI, parseURI, escapeURIString) -import Network.HTTP +import Network.URI (isAbsoluteURI, escapeURIString) import Data.ByteString.Base64 import qualified Data.ByteString.Char8 as B import Data.ByteString (ByteString) -import Data.ByteString.UTF8 (toString, fromString) import System.FilePath (takeExtension, dropExtension, takeDirectory, (</>)) import Data.Char (toLower, isAscii, isAlphaNum) import Codec.Compression.GZip as Gzip import qualified Data.ByteString.Lazy as L -import Text.Pandoc.Shared (findDataFile) +import Text.Pandoc.Shared (renderTags', openURL, readDataFile) +import Text.Pandoc.UTF8 (toString, fromString) import Text.Pandoc.MIME (getMimeType) import System.Directory (doesFileExist) -getItem :: Maybe FilePath -> String -> IO (ByteString, Maybe String) -getItem userdata f = - if isAbsoluteURI f - then openURL f - else do - let mime = case takeExtension f of - ".gz" -> getMimeType $ dropExtension f - x -> getMimeType x - exists <- doesFileExist f - if exists - then do - cont <- B.readFile f - return (cont, mime) - else do - res <- findDataFile userdata f - exists' <- doesFileExist res - if exists' - then do - cont <- B.readFile res - return (cont, mime) - else error $ "Could not find `" ++ f ++ "'" - --- TODO - have this return mime type too - then it can work for google --- chart API, e.g. -openURL :: String -> IO (ByteString, Maybe String) -openURL u = getBodyAndMimeType =<< simpleHTTP (getReq u) - where getReq v = case parseURI v of - Nothing -> error $ "Could not parse URI: " ++ v - Just u' -> mkRequest GET u' - getBodyAndMimeType (Left err) = fail (show err) - getBodyAndMimeType (Right r) = return (rspBody r, findHeader HdrContentType r) - isOk :: Char -> Bool isOk c = isAscii c && isAlphaNum c @@ -102,14 +69,14 @@ convertTag userdata t@(TagOpen "script" as) = src -> do (raw, mime) <- getRaw userdata (fromAttrib "type" t) src let enc = "data:" ++ mime ++ "," ++ escapeURIString isOk (toString raw) - return $ TagOpen "script" (("src",enc) : [(x,y) | (x,y) <- as, x /= "src"]) + return $ TagOpen "script" (("src",enc) : [(x,y) | (x,y) <- as, x /= "src"]) convertTag userdata t@(TagOpen "link" as) = case fromAttrib "href" t of [] -> return t src -> do (raw, mime) <- getRaw userdata (fromAttrib "type" t) src let enc = "data:" ++ mime ++ "," ++ escapeURIString isOk (toString raw) - return $ TagOpen "link" (("href",enc) : [(x,y) | (x,y) <- as, x /= "href"]) + return $ TagOpen "link" (("href",enc) : [(x,y) | (x,y) <- as, x /= "href"]) convertTag _ t = return t cssURLs :: Maybe FilePath -> FilePath -> ByteString -> IO ByteString @@ -132,6 +99,18 @@ cssURLs userdata d orig = ";base64," `B.append` (encode raw) return $ x `B.append` "url(" `B.append` enc `B.append` rest +getItem :: Maybe FilePath -> String -> IO (ByteString, Maybe String) +getItem userdata f = + if isAbsoluteURI f + then openURL f + else do + let mime = case takeExtension f of + ".gz" -> getMimeType $ dropExtension f + x -> getMimeType x + exists <- doesFileExist f + cont <- if exists then B.readFile f else readDataFile userdata f + return (cont, mime) + getRaw :: Maybe FilePath -> String -> String -> IO (ByteString, String) getRaw userdata mimetype src = do let ext = map toLower $ takeExtension src @@ -163,14 +142,3 @@ makeSelfContained userdata inp = do out' <- mapM (convertTag userdata) tags return $ renderTags' out' --- repeated from HTML reader: -renderTags' :: [Tag String] -> String -renderTags' = renderTagsOptions - renderOptions{ optMinimize = \x -> - let y = map toLower x - in y == "hr" || y == "br" || - y == "img" || y == "meta" || - y == "link" - , optRawTag = \x -> - let y = map toLower x - in y == "script" || y == "style" } diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index f14a57c1f..26b0e1b1d 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveDataTypeable, CPP, TemplateHaskell #-} {- Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu> @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Shared Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -38,9 +38,9 @@ module Text.Pandoc.Shared ( backslashEscapes, escapeStringUsing, stripTrailingNewlines, - removeLeadingTrailingSpace, - removeLeadingSpace, - removeTrailingSpace, + trim, + triml, + trimr, stripFirstAndLast, camelCaseToHyphenated, toRomanNumeral, @@ -54,47 +54,59 @@ module Text.Pandoc.Shared ( normalize, stringify, compactify, + compactify', Element (..), hierarchicalize, uniqueIdent, isHeaderBlock, headerShift, - -- * Writer options - HTMLMathMethod (..), - CiteMethod (..), - ObfuscationMethod (..), - HTMLSlideVariant (..), - WriterOptions (..), - defaultWriterOptions, + isTightList, + -- * TagSoup HTML handling + renderTags', -- * File handling inDirectory, - findDataFile, readDataFile, + readDataFileUTF8, + fetchItem, + openURL, -- * Error handling err, warn, + -- * Safe read + safeRead ) where import Text.Pandoc.Definition import Text.Pandoc.Generic +import Text.Pandoc.Builder (Blocks) +import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 import System.Environment (getProgName) import System.Exit (exitWith, ExitCode(..)) import Data.Char ( toLower, isLower, isUpper, isAlpha, isLetter, isDigit, isSpace ) import Data.List ( find, isPrefixOf, intercalate ) -import Network.URI ( escapeURIString ) +import Network.URI ( escapeURIString, isAbsoluteURI, parseURI ) import System.Directory -import System.FilePath ( (</>) ) +import Text.Pandoc.MIME (getMimeType) +import System.FilePath ( (</>), takeExtension, dropExtension ) import Data.Generics (Typeable, Data) import qualified Control.Monad.State as S import Control.Monad (msum) -import Paths_pandoc (getDataFileName) -import Text.Pandoc.Highlighting (Style, pygments) import Text.Pandoc.Pretty (charWidth) import System.Locale (defaultTimeLocale) import Data.Time import System.IO (stderr) +import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..), + renderOptions) +import qualified Data.ByteString as B +import Network.HTTP (findHeader, rspBody, simpleHTTP, RequestMethod(..), + HeaderName(..), mkRequest) +#ifdef EMBED_DATA_FILES +import Data.FileEmbed +#else +import Paths_pandoc (getDataFileName) +#endif -- -- List processing @@ -149,7 +161,7 @@ backslashEscapes = map (\ch -> (ch, ['\\',ch])) -- characters and strings. escapeStringUsing :: [(Char, String)] -> String -> String escapeStringUsing _ [] = "" -escapeStringUsing escapeTable (x:xs) = +escapeStringUsing escapeTable (x:xs) = case (lookup x escapeTable) of Just str -> str ++ rest Nothing -> x:rest @@ -160,23 +172,23 @@ stripTrailingNewlines :: String -> String stripTrailingNewlines = reverse . dropWhile (== '\n') . reverse -- | Remove leading and trailing space (including newlines) from string. -removeLeadingTrailingSpace :: String -> String -removeLeadingTrailingSpace = removeLeadingSpace . removeTrailingSpace +trim :: String -> String +trim = triml . trimr -- | Remove leading space (including newlines) from string. -removeLeadingSpace :: String -> String -removeLeadingSpace = dropWhile (`elem` " \n\t") +triml :: String -> String +triml = dropWhile (`elem` " \r\n\t") -- | Remove trailing space (including newlines) from string. -removeTrailingSpace :: String -> String -removeTrailingSpace = reverse . removeLeadingSpace . reverse +trimr :: String -> String +trimr = reverse . triml . reverse -- | Strip leading and trailing characters from string stripFirstAndLast :: String -> String stripFirstAndLast str = drop 1 $ take ((length str) - 1) str --- | Change CamelCase word to hyphenated lowercase (e.g., camel-case). +-- | Change CamelCase word to hyphenated lowercase (e.g., camel-case). camelCaseToHyphenated :: String -> String camelCaseToHyphenated [] = "" camelCaseToHyphenated (a:b:rest) | isLower a && isUpper b = @@ -247,13 +259,13 @@ normalizeDate s = fmap (formatTime defaultTimeLocale "%F") -- | Generate infinite lazy list of markers for an ordered list, -- depending on list attributes. orderedListMarkers :: (Int, ListNumberStyle, ListNumberDelim) -> [String] -orderedListMarkers (start, numstyle, numdelim) = +orderedListMarkers (start, numstyle, numdelim) = let singleton c = [c] nums = case numstyle of DefaultStyle -> map show [start..] Example -> map show [start..] Decimal -> map show [start..] - UpperAlpha -> drop (start - 1) $ cycle $ + UpperAlpha -> drop (start - 1) $ cycle $ map singleton ['A'..'Z'] LowerAlpha -> drop (start - 1) $ cycle $ map singleton ['a'..'z'] @@ -271,13 +283,12 @@ orderedListMarkers (start, numstyle, numdelim) = -- remove empty Str elements. normalizeSpaces :: [Inline] -> [Inline] normalizeSpaces = cleanup . dropWhile isSpaceOrEmpty - where cleanup [] = [] - cleanup (Space:rest) = let rest' = dropWhile isSpaceOrEmpty rest - in case rest' of - [] -> [] - _ -> Space : cleanup rest' + where cleanup [] = [] + cleanup (Space:rest) = case dropWhile isSpaceOrEmpty rest of + [] -> [] + (x:xs) -> Space : x : cleanup xs cleanup ((Str ""):rest) = cleanup rest - cleanup (x:rest) = x : cleanup rest + cleanup (x:rest) = x : cleanup rest isSpaceOrEmpty :: Inline -> Bool isSpaceOrEmpty Space = True @@ -381,12 +392,27 @@ compactify items = _ -> items _ -> items +-- | Change final list item from @Para@ to @Plain@ if the list contains +-- no other @Para@ blocks. Like compactify, but operates on @Blocks@ rather +-- than @[Block]@. +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) -> case [Para x | Para x <- concatMap B.toList items] of + -- if this is only Para, change to Plain + [_] -> others ++ [B.fromList (reverse $ Plain a : xs)] + _ -> items + _ -> items + isPara :: Block -> Bool isPara (Para _) = True isPara _ = False -- | Data structure for defining hierarchical Pandoc documents -data Element = Blk Block +data Element = Blk Block | Sec Int [Int] String [Inline] [Element] -- lvl num ident label contents deriving (Eq, Read, Show, Typeable, Data) @@ -405,18 +431,17 @@ inlineListToIdentifier = -- | Convert list of Pandoc blocks into (hierarchical) list of Elements hierarchicalize :: [Block] -> [Element] -hierarchicalize blocks = S.evalState (hierarchicalizeWithIds blocks) ([],[]) +hierarchicalize blocks = S.evalState (hierarchicalizeWithIds blocks) [] -hierarchicalizeWithIds :: [Block] -> S.State ([Int],[String]) [Element] +hierarchicalizeWithIds :: [Block] -> S.State [Int] [Element] hierarchicalizeWithIds [] = return [] -hierarchicalizeWithIds ((Header level title'):xs) = do - (lastnum, usedIdents) <- S.get - let ident = uniqueIdent title' usedIdents +hierarchicalizeWithIds ((Header level (ident,_,_) title'):xs) = do + lastnum <- S.get let lastnum' = take level lastnum let newnum = if length lastnum' >= level - then init lastnum' ++ [last lastnum' + 1] + then init lastnum' ++ [last lastnum' + 1] else lastnum ++ replicate (level - length lastnum - 1) 0 ++ [1] - S.put (newnum, (ident : usedIdents)) + S.put newnum let (sectionContents, rest) = break (headerLtEq level) xs sectionContents' <- hierarchicalizeWithIds sectionContents rest' <- hierarchicalizeWithIds rest @@ -426,7 +451,7 @@ hierarchicalizeWithIds (x:rest) = do return $ (Blk x) : rest' headerLtEq :: Int -> Block -> Bool -headerLtEq level (Header l _) = l <= level +headerLtEq level (Header l _ _) = l <= level headerLtEq _ _ = False -- | Generate a unique identifier from a list of inlines. @@ -445,123 +470,37 @@ uniqueIdent title' usedIdents = -- | True if block is a Header block. isHeaderBlock :: Block -> Bool -isHeaderBlock (Header _ _) = True +isHeaderBlock (Header _ _ _) = True isHeaderBlock _ = False -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc headerShift n = bottomUp shift where shift :: Block -> Block - shift (Header level inner) = Header (level + n) inner - shift x = x + shift (Header level attr inner) = Header (level + n) attr inner + shift x = x + +-- | Detect if a list is tight. +isTightList :: [[Block]] -> Bool +isTightList = and . map firstIsPlain + where firstIsPlain (Plain _ : _) = True + firstIsPlain _ = False -- --- Writer options +-- TagSoup HTML handling -- -data HTMLMathMethod = PlainMath - | LaTeXMathML (Maybe String) -- url of LaTeXMathML.js - | JsMath (Maybe String) -- url of jsMath load script - | GladTeX - | WebTeX String -- url of TeX->image script. - | MathML (Maybe String) -- url of MathMLinHTML.js - | MathJax String -- url of MathJax.js - deriving (Show, Read, Eq) - -data CiteMethod = Citeproc -- use citeproc to render them - | Natbib -- output natbib cite commands - | Biblatex -- output biblatex cite commands - deriving (Show, Read, Eq) - --- | Methods for obfuscating email addresses in HTML. -data ObfuscationMethod = NoObfuscation - | ReferenceObfuscation - | JavascriptObfuscation - deriving (Show, Read, Eq) - --- | Varieties of HTML slide shows. -data HTMLSlideVariant = S5Slides - | SlidySlides - | SlideousSlides - | DZSlides - | NoSlides - deriving (Show, Read, Eq) - --- | Options for writers -data WriterOptions = WriterOptions - { writerStandalone :: Bool -- ^ Include header and footer - , writerTemplate :: String -- ^ Template to use in standalone mode - , writerVariables :: [(String, String)] -- ^ Variables to set in template - , writerEPUBMetadata :: String -- ^ Metadata to include in EPUB - , writerTabStop :: Int -- ^ Tabstop for conversion btw spaces and tabs - , writerTableOfContents :: Bool -- ^ Include table of contents - , writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous? - , writerIncremental :: Bool -- ^ True if lists should be incremental - , writerXeTeX :: Bool -- ^ Create latex suitable for use by xetex - , writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML - , writerIgnoreNotes :: Bool -- ^ Ignore footnotes (used in making toc) - , writerNumberSections :: Bool -- ^ Number sections in LaTeX - , writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML - , writerStrictMarkdown :: Bool -- ^ Use strict markdown syntax - , writerReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst - , writerWrapText :: Bool -- ^ Wrap text to line length - , writerColumns :: Int -- ^ Characters in a line (for text wrapping) - , writerLiterateHaskell :: Bool -- ^ Write as literate haskell - , writerEmailObfuscation :: ObfuscationMethod -- ^ How to obfuscate emails - , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML - , writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file - , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory - , writerCiteMethod :: CiteMethod -- ^ How to print cites - , writerBiblioFiles :: [FilePath] -- ^ Biblio files to use for citations - , writerHtml5 :: Bool -- ^ Produce HTML5 - , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show - , writerSlideLevel :: Maybe Int -- ^ Force header level of slides - , writerChapters :: Bool -- ^ Use "chapter" for top-level sects - , writerListings :: Bool -- ^ Use listings package for code - , writerHighlight :: Bool -- ^ Highlight source code - , writerHighlightStyle :: Style -- ^ Style to use for highlighting - , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown - , writerTeXLigatures :: Bool -- ^ Use tex ligatures quotes, dashes in latex - } deriving Show - -{-# DEPRECATED writerXeTeX "writerXeTeX no longer does anything" #-} --- | Default writer options. -defaultWriterOptions :: WriterOptions -defaultWriterOptions = - WriterOptions { writerStandalone = False - , writerTemplate = "" - , writerVariables = [] - , writerEPUBMetadata = "" - , writerTabStop = 4 - , writerTableOfContents = False - , writerSlideVariant = NoSlides - , writerIncremental = False - , writerXeTeX = False - , writerHTMLMathMethod = PlainMath - , writerIgnoreNotes = False - , writerNumberSections = False - , writerSectionDivs = False - , writerStrictMarkdown = False - , writerReferenceLinks = False - , writerWrapText = True - , writerColumns = 72 - , writerLiterateHaskell = False - , writerEmailObfuscation = JavascriptObfuscation - , writerIdentifierPrefix = "" - , writerSourceDirectory = "." - , writerUserDataDir = Nothing - , writerCiteMethod = Citeproc - , writerBiblioFiles = [] - , writerHtml5 = False - , writerBeamer = False - , writerSlideLevel = Nothing - , writerChapters = False - , writerListings = False - , writerHighlight = False - , writerHighlightStyle = pygments - , writerSetextHeaders = True - , writerTeXLigatures = True - } +-- | Render HTML tags. +renderTags' :: [Tag String] -> String +renderTags' = renderTagsOptions + renderOptions{ optMinimize = \x -> + let y = map toLower x + in y == "hr" || y == "br" || + y == "img" || y == "meta" || + y == "link" + , optRawTag = \x -> + let y = map toLower x + in y == "script" || y == "style" } -- -- File handling @@ -576,20 +515,62 @@ inDirectory path action = do setCurrentDirectory oldDir return result --- | Get file path for data file, either from specified user data directory, --- or, if not found there, from Cabal data directory. -findDataFile :: Maybe FilePath -> FilePath -> IO FilePath -findDataFile Nothing f = getDataFileName f -findDataFile (Just u) f = do - ex <- doesFileExist (u </> f) - if ex - then return (u </> f) - else getDataFileName f +#ifdef EMBED_DATA_FILES +dataFiles :: [(FilePath, B.ByteString)] +dataFiles = $(embedDir "data") +#endif + +readDefaultDataFile :: FilePath -> IO B.ByteString +readDefaultDataFile fname = +#ifdef EMBED_DATA_FILES + case lookup fname dataFiles of + Nothing -> ioError $ userError + $ "Data file `" ++ fname ++ "' does not exist" + Just contents -> return contents +#else + getDataFileName ("data" </> fname) >>= B.readFile +#endif -- | Read file from specified user data directory or, if not found there, from -- Cabal data directory. -readDataFile :: Maybe FilePath -> FilePath -> IO String -readDataFile userDir fname = findDataFile userDir fname >>= UTF8.readFile +readDataFile :: Maybe FilePath -> FilePath -> IO B.ByteString +readDataFile Nothing fname = readDefaultDataFile fname +readDataFile (Just userDir) fname = do + exists <- doesFileExist (userDir </> fname) + if exists + then B.readFile (userDir </> fname) + else readDefaultDataFile fname + +-- | Same as 'readDataFile' but returns a String instead of a ByteString. +readDataFileUTF8 :: Maybe FilePath -> FilePath -> IO String +readDataFileUTF8 userDir fname = + UTF8.toString `fmap` readDataFile userDir fname + +-- | Fetch an image or other item from the local filesystem or the net. +-- Returns raw content and maybe mime type. +fetchItem :: String -> String -> IO (B.ByteString, Maybe String) +fetchItem sourceDir s = + case s of + _ | isAbsoluteURI s -> openURL s + | isAbsoluteURI sourceDir -> openURL $ sourceDir ++ "/" ++ s + | otherwise -> do + let mime = case takeExtension s of + ".gz" -> getMimeType $ dropExtension s + x -> getMimeType x + let f = sourceDir </> s + cont <- B.readFile f + return (cont, mime) + +-- TODO - have this return mime type too - then it can work for google +-- chart API, e.g. +-- | Read from a URL and return raw data and maybe mime type. +openURL :: String -> IO (B.ByteString, Maybe String) +openURL u = getBodyAndMimeType =<< simpleHTTP (getReq u) + where getReq v = case parseURI v of + Nothing -> error $ "Could not parse URI: " ++ v + Just u' -> mkRequest GET u' + getBodyAndMimeType (Left e) = fail (show e) + getBodyAndMimeType (Right r) = return (rspBody r, findHeader HdrContentType r) -- -- Error reporting @@ -606,3 +587,15 @@ warn :: String -> IO () warn msg = do name <- getProgName UTF8.hPutStrLn stderr $ name ++ ": " ++ msg + +-- +-- Safe read +-- + +safeRead :: (Monad m, Read a) => String -> m a +safeRead s = case reads s of + (d,x):_ + | all isSpace x -> return d + _ -> fail $ "Could not read `" ++ s ++ "'" + + diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index fe9b60720..b69057b7a 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -35,24 +35,24 @@ import Text.Pandoc.Definition -- 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) + where go least (Header n _ _ : x : xs) | n < least && nonHOrHR x = go n xs | otherwise = go least (x:xs) go least (_ : xs) = go least xs go least [] = least - nonHOrHR (Header _ _) = False + nonHOrHR (Header _ _ _) = False nonHOrHR (HorizontalRule) = False nonHOrHR _ = True -- | Prepare a block list to be passed to hierarchicalize. prepSlides :: Int -> [Block] -> [Block] prepSlides slideLevel = ensureStartWithH . splitHrule - where splitHrule (HorizontalRule : Header n xs : ys) - | n == slideLevel = Header slideLevel xs : splitHrule ys - splitHrule (HorizontalRule : xs) = Header slideLevel [Str "\0"] : + 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 [] = [] - ensureStartWithH bs@(Header n _:_) + ensureStartWithH bs@(Header n _ _:_) | n <= slideLevel = bs - ensureStartWithH bs = Header slideLevel [Str "\0"] : bs + ensureStartWithH bs = Header slideLevel nullAttr [Str "\0"] : bs diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index dfdcd8e63..bbdb4adc4 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -30,7 +30,7 @@ A simple templating system with variable substitution and conditionals. Example: > renderTemplate [("name","Sam"),("salary","50,000")] $ -> "Hi, $name$. $if(salary)$You make $$$salary$.$else$No salary data.$endif$" +> "Hi, $name$. $if(salary)$You make $$$salary$.$else$No salary data.$endif$" > "Hi, John. You make $50,000." A slot for an interpolated variable is a variable name surrounded @@ -68,8 +68,8 @@ module Text.Pandoc.Templates ( renderTemplate , TemplateTarget , getDefaultTemplate ) where -import Text.ParserCombinators.Parsec -import Control.Monad (liftM, when, forM) +import Text.Parsec +import Control.Monad (liftM, when, forM, mzero) import System.FilePath import Data.List (intercalate, intersperse) #if MIN_VERSION_blaze_html(0,5,0) @@ -78,27 +78,31 @@ import Text.Blaze.Internal (preEscapedString) #else import Text.Blaze (preEscapedString, Html) #endif -import Data.ByteString.Lazy.UTF8 (ByteString, fromString) -import Text.Pandoc.Shared (readDataFile) +import Text.Pandoc.UTF8 (fromStringLazy) +import Data.ByteString.Lazy (ByteString) +import Text.Pandoc.Shared (readDataFileUTF8) import qualified Control.Exception.Extensible as E (try, IOException) -- | Get default template for the specified writer. -getDefaultTemplate :: (Maybe FilePath) -- ^ User data directory to search first - -> String -- ^ Name of writer +getDefaultTemplate :: (Maybe FilePath) -- ^ User data directory to search first + -> String -- ^ Name of writer -> IO (Either E.IOException String) -getDefaultTemplate _ "native" = return $ Right "" -getDefaultTemplate _ "json" = return $ Right "" -getDefaultTemplate _ "docx" = return $ Right "" -getDefaultTemplate user "odt" = getDefaultTemplate user "opendocument" -getDefaultTemplate user "epub" = getDefaultTemplate user "html" getDefaultTemplate user writer = do - let format = takeWhile (/='+') writer -- strip off "+lhs" if present - let fname = "templates" </> "default" <.> format - E.try $ readDataFile user fname + let format = takeWhile (`notElem` "+-") writer -- strip off extensions + case format of + "native" -> return $ Right "" + "json" -> return $ Right "" + "docx" -> return $ Right "" + "odt" -> getDefaultTemplate user "opendocument" + "markdown_strict" -> getDefaultTemplate user "markdown" + "multimarkdown" -> getDefaultTemplate user "markdown" + "markdown_github" -> getDefaultTemplate user "markdown" + _ -> let fname = "templates" </> "default" <.> format + in E.try $ readDataFileUTF8 user fname data TemplateState = TemplateState Int [(String,String)] -adjustPosition :: String -> GenParser Char TemplateState String +adjustPosition :: String -> Parsec [Char] TemplateState String adjustPosition str = do let lastline = takeWhile (/= '\n') $ reverse str updateState $ \(TemplateState pos x) -> @@ -108,18 +112,18 @@ adjustPosition str = do return str class TemplateTarget a where - toTarget :: String -> a + toTarget :: String -> a instance TemplateTarget String where toTarget = id -instance TemplateTarget ByteString where - toTarget = fromString +instance TemplateTarget ByteString where + toTarget = fromStringLazy instance TemplateTarget Html where toTarget = preEscapedString --- | Renders a template +-- | Renders a template renderTemplate :: TemplateTarget a => [(String,String)] -- ^ Assoc. list of values for variables -> String -- ^ Template @@ -132,21 +136,21 @@ renderTemplate vals templ = reservedWords :: [String] reservedWords = ["else","endif","for","endfor","sep"] -parseTemplate :: GenParser Char TemplateState [String] +parseTemplate :: Parsec [Char] TemplateState [String] parseTemplate = many $ (plaintext <|> escapedDollar <|> conditional <|> for <|> variable) >>= adjustPosition -plaintext :: GenParser Char TemplateState String +plaintext :: Parsec [Char] TemplateState String plaintext = many1 $ noneOf "$" -escapedDollar :: GenParser Char TemplateState String +escapedDollar :: Parsec [Char] TemplateState String escapedDollar = try $ string "$$" >> return "$" -skipEndline :: GenParser Char st () +skipEndline :: Parsec [Char] st () skipEndline = try $ skipMany (oneOf " \t") >> newline >> return () -conditional :: GenParser Char TemplateState String +conditional :: Parsec [Char] TemplateState String conditional = try $ do TemplateState pos vars <- getState string "$if(" @@ -170,7 +174,7 @@ conditional = try $ do then ifContents else elseContents -for :: GenParser Char TemplateState String +for :: Parsec [Char] TemplateState String for = try $ do TemplateState pos vars <- getState string "$for(" @@ -178,14 +182,14 @@ for = try $ do string ")$" -- if newline after the "for", then a newline after "endfor" will be swallowed multiline <- option False $ try $ skipEndline >> return True - let matches = filter (\(k,_) -> k == id') vars + let matches = filter (\(k,_) -> k == id') vars let indent = replicate pos ' ' contents <- forM matches $ \m -> do updateState $ \(TemplateState p v) -> TemplateState p (m:v) raw <- liftM concat $ lookAhead parseTemplate return $ intercalate ('\n':indent) $ lines $ raw ++ "\n" parseTemplate - sep <- option "" $ do try (string "$sep$") + sep <- option "" $ do try (string "$sep$") when multiline $ optional skipEndline liftM concat parseTemplate string "$endfor$" @@ -193,16 +197,16 @@ for = try $ do setState $ TemplateState pos vars return $ concat $ intersperse sep contents -ident :: GenParser Char TemplateState String +ident :: Parsec [Char] TemplateState String ident = do first <- letter rest <- many (alphaNum <|> oneOf "_-") let id' = first : rest if id' `elem` reservedWords - then pzero + then mzero else return id' -variable :: GenParser Char TemplateState String +variable :: Parsec [Char] TemplateState String variable = try $ do char '$' id' <- ident diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index e2959eae7..582afb6dc 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UTF8 Copyright : Copyright (C) 2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -35,21 +35,27 @@ module Text.Pandoc.UTF8 ( readFile , hPutStr , hPutStrLn , hGetContents + , toString + , fromString + , toStringLazy + , fromStringLazy + , encodePath + , decodeArg ) where -#if MIN_VERSION_base(4,4,0) -#else -import Codec.Binary.UTF8.String (encodeString) -#endif - -#if MIN_VERSION_base(4,2,0) - import System.IO hiding (readFile, writeFile, getContents, putStr, putStrLn, hPutStr, hPutStrLn, hGetContents) -import Prelude hiding (readFile, writeFile, getContents, putStr, putStrLn ) +import Prelude hiding (readFile, writeFile, getContents, putStr, putStrLn, + catch) import qualified System.IO as IO +import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Lazy as BL +import qualified Data.Text.Encoding as T +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TL readFile :: FilePath -> IO String readFile f = do @@ -75,53 +81,33 @@ hPutStrLn :: Handle -> String -> IO () hPutStrLn h s = hSetEncoding h utf8 >> IO.hPutStrLn h s hGetContents :: Handle -> IO String -hGetContents h = hSetEncoding h utf8_bom >> IO.hGetContents h - -#else +hGetContents = fmap toStringLazy . BL.hGetContents +-- hGetContents h = hSetEncoding h utf8_bom +-- >> hSetNewlineMode h universalNewlineMode +-- >> IO.hGetContents h -import qualified Data.ByteString as B -import Data.ByteString.UTF8 (toString, fromString) -import Prelude hiding (readFile, writeFile, getContents, putStr, putStrLn) -import System.IO (Handle) -import Control.Monad (liftM) +-- | Convert UTF8-encoded ByteString to String, also +-- removing '\r' characters. +toString :: B.ByteString -> String +toString = filter (/='\r') . T.unpack . T.decodeUtf8 +fromString :: String -> B.ByteString +fromString = T.encodeUtf8 . T.pack -bom :: B.ByteString -bom = B.pack [0xEF, 0xBB, 0xBF] +-- | Convert UTF8-encoded ByteString to String, also +-- removing '\r' characters. +toStringLazy :: BL.ByteString -> String +toStringLazy = filter (/='\r') . TL.unpack . TL.decodeUtf8 -stripBOM :: B.ByteString -> B.ByteString -stripBOM s | bom `B.isPrefixOf` s = B.drop 3 s -stripBOM s = s - -readFile :: FilePath -> IO String -readFile = liftM (toString . stripBOM) . B.readFile . encodePath - -writeFile :: FilePath -> String -> IO () -writeFile f = B.writeFile (encodePath f) . fromString - -getContents :: IO String -getContents = liftM (toString . stripBOM) B.getContents - -hGetContents :: Handle -> IO String -hGetContents h = liftM (toString . stripBOM) (B.hGetContents h) - -putStr :: String -> IO () -putStr = B.putStr . fromString - -putStrLn :: String -> IO () -putStrLn = B.putStrLn . fromString - -hPutStr :: Handle -> String -> IO () -hPutStr h = B.hPutStr h . fromString - -hPutStrLn :: Handle -> String -> IO () -hPutStrLn h s = hPutStr h (s ++ "\n") - -#endif +fromStringLazy :: String -> BL.ByteString +fromStringLazy = TL.encodeUtf8 . TL.pack encodePath :: FilePath -> FilePath +decodeArg :: String -> String #if MIN_VERSION_base(4,4,0) encodePath = id +decodeArg = id #else -encodePath = encodeString +encodePath = B.unpack . fromString +decodeArg = toString . B.pack #endif diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 1913eb92b..3718431e2 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -40,8 +40,8 @@ module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc) where import Text.Pandoc.Definition import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Shared -import Text.Pandoc.Parsing hiding (blankline) -import Text.ParserCombinators.Parsec ( runParser, GenParser ) +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (blankline, space) import Data.List ( isPrefixOf, intersperse, intercalate ) import Text.Pandoc.Pretty import Control.Monad.State @@ -93,7 +93,7 @@ escapeString = escapeStringUsing escs where escs = backslashEscapes "{" -- | Ordered list start parser for use in Para below. -olMarker :: GenParser Char ParserState Char +olMarker :: Parser [Char] ParserState Char olMarker = do (start, style', delim) <- anyOrderedListMarker if delim == Period && (style' == UpperAlpha || (style' == UpperRoman && @@ -116,6 +116,8 @@ blockToAsciiDoc _ Null = return empty blockToAsciiDoc opts (Plain inlines) = do contents <- inlineListToAsciiDoc opts inlines return $ contents <> cr +blockToAsciiDoc opts (Para [Image alt (src,'f':'i':'g':':':tit)]) = + blockToAsciiDoc opts (Para [Image alt (src,tit)]) blockToAsciiDoc opts (Para inlines) = do contents <- inlineListToAsciiDoc opts inlines -- escape if para starts with ordered list marker @@ -126,10 +128,10 @@ blockToAsciiDoc opts (Para inlines) = do blockToAsciiDoc _ (RawBlock _ _) = return empty blockToAsciiDoc _ HorizontalRule = return $ blankline <> text "'''''" <> blankline -blockToAsciiDoc opts (Header level inlines) = do +blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do contents <- inlineListToAsciiDoc opts inlines let len = offset contents - return $ contents <> cr <> + return $ ("[[" <> text ident <> "]]") $$ contents $$ (case level of 1 -> text $ replicate len '-' 2 -> text $ replicate len '~' @@ -343,8 +345,8 @@ inlineToAsciiDoc opts (Link txt (src, _tit)) = do else empty let srcSuffix = if isPrefixOf "mailto:" src then drop 7 src else src let useAuto = case txt of - [Code _ s] | s == srcSuffix -> True - _ -> False + [Str s] | escapeURI s == srcSuffix -> True + _ -> False return $ if useAuto then text srcSuffix else prefix <> text src <> "[" <> linktext <> "]" diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 964320eb2..dd9979290 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -20,10 +20,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ConTeXt Copyright : Copyright (C) 2007-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha + Stability : alpha Portability : portable Conversion of 'Pandoc' format into ConTeXt. @@ -31,31 +31,32 @@ Conversion of 'Pandoc' format into ConTeXt. module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Text.Pandoc.Definition import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Generic (queryWith) import Text.Printf ( printf ) -import Data.List ( intercalate ) +import Data.List ( intercalate, isPrefixOf ) import Control.Monad.State import Text.Pandoc.Pretty import Text.Pandoc.Templates ( renderTemplate ) import Network.URI ( isURI, unEscapeString ) -data WriterState = +data WriterState = WriterState { stNextRef :: Int -- number of next URL reference , stOrderedListLevel :: Int -- level of ordered list , stOptions :: WriterOptions -- writer options } -orderedListStyles :: [[Char]] -orderedListStyles = cycle ["[n]","[a]", "[r]", "[g]"] +orderedListStyles :: [Char] +orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. writeConTeXt :: WriterOptions -> Pandoc -> String -writeConTeXt options document = +writeConTeXt options document = let defaultWriterState = WriterState { stNextRef = 1 , stOrderedListLevel = 0 , stOptions = options - } - in evalState (pandocToConTeXt options document) defaultWriterState + } + in evalState (pandocToConTeXt options document) defaultWriterState pandocToConTeXt :: WriterOptions -> Pandoc -> State WriterState String pandocToConTeXt options (Pandoc (Meta title authors date) blocks) = do @@ -73,6 +74,12 @@ pandocToConTeXt options (Pandoc (Meta title authors date) blocks) = do let main = (render colwidth . vcat) body let context = writerVariables options ++ [ ("toc", if writerTableOfContents options then "yes" else "") + , ("placelist", intercalate "," $ + take (writerTOCDepth options + if writerChapters options + then 0 + else 1) + ["chapter","section","subsection","subsubsection", + "subsubsubsection","subsubsubsubsection"]) , ("body", main) , ("title", titletext) , ("date", datetext) ] ++ @@ -120,15 +127,16 @@ elementToConTeXt opts (Sec level _ id' title' elements) = do return $ vcat (header' : innerContents) -- | Convert Pandoc block element to ConTeXt. -blockToConTeXt :: Block +blockToConTeXt :: Block -> State WriterState Doc blockToConTeXt Null = return empty blockToConTeXt (Plain lst) = inlineListToConTeXt lst -blockToConTeXt (Para [Image txt (src,_)]) = do +-- title beginning with fig: indicates that the image is a figure +blockToConTeXt (Para [Image txt (src,'f':'i':'g':':':_)]) = do capt <- inlineListToConTeXt txt return $ blankline $$ "\\placefigure[here,nonumber]" <> braces capt <> braces ("\\externalfigure" <> brackets (text src)) <> blankline -blockToConTeXt (Para lst) = do +blockToConTeXt (Para lst) = do contents <- inlineListToConTeXt lst return $ contents <> blankline blockToConTeXt (BlockQuote lst) = do @@ -141,37 +149,41 @@ blockToConTeXt (RawBlock "context" str) = return $ text str <> blankline blockToConTeXt (RawBlock _ _ ) = return empty blockToConTeXt (BulletList lst) = do contents <- mapM listItemToConTeXt lst - return $ "\\startitemize" $$ vcat contents $$ text "\\stopitemize" <> blankline + return $ ("\\startitemize" <> if isTightList lst + then brackets "packed" + else empty) $$ + vcat contents $$ text "\\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} + put $ st {stOrderedListLevel = level} let start' = if start == 1 then "" else "start=" ++ show start let delim' = case delim of DefaultDelim -> "" - Period -> "stopper=." - OneParen -> "stopper=)" + Period -> "stopper=." + OneParen -> "stopper=)" TwoParens -> "left=(,stopper=)" - let width = maximum $ map length $ take (length contents) + let width = maximum $ map length $ take (length contents) (orderedListMarkers (start, style', delim)) let width' = (toEnum width + 1) / 2 - let width'' = if width' > (1.5 :: Double) - then "width=" ++ show width' ++ "em" + let width'' = if width' > (1.5 :: Double) + then "width=" ++ show width' ++ "em" else "" let specs2Items = filter (not . null) [start', delim', width''] let specs2 = if null specs2Items then "" else "[" ++ intercalate "," specs2Items ++ "]" - let style'' = case style' of - DefaultStyle -> orderedListStyles !! level - Decimal -> "[n]" - Example -> "[n]" - LowerRoman -> "[r]" - UpperRoman -> "[R]" - LowerAlpha -> "[a]" - UpperAlpha -> "[A]" + 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 = style'' ++ specs2 return $ "\\startitemize" <> text specs $$ vcat contents $$ "\\stopitemize" <> blankline @@ -179,24 +191,24 @@ 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 lst) = sectionHeader "" level lst +blockToConTeXt (Header level (ident,_,_) lst) = sectionHeader ident level lst blockToConTeXt (Table caption aligns widths heads rows) = do let colDescriptor colWidth alignment = (case alignment of - AlignLeft -> 'l' + AlignLeft -> 'l' AlignRight -> 'r' AlignCenter -> 'c' AlignDefault -> 'l'): if colWidth == 0 then "|" else ("p(" ++ printf "%.2f" colWidth ++ "\\textwidth)|") - let colDescriptors = "|" ++ (concat $ + let colDescriptors = "|" ++ (concat $ zipWith colDescriptor widths aligns) headers <- if all null heads then return empty - else liftM ($$ "\\HL") $ tableRowToConTeXt heads - captionText <- inlineListToConTeXt caption + else liftM ($$ "\\HL") $ tableRowToConTeXt heads + captionText <- inlineListToConTeXt caption let captionText' = if null caption then text "none" else captionText - rows' <- mapM tableRowToConTeXt rows + rows' <- mapM tableRowToConTeXt rows return $ "\\placetable[here]" <> braces captionText' $$ "\\starttable" <> brackets (text colDescriptors) $$ "\\HL" $$ headers $$ @@ -230,7 +242,7 @@ inlineListToConTeXt lst = liftM hcat $ mapM inlineToConTeXt lst -- | Convert inline element to ConTeXt inlineToConTeXt :: Inline -- ^ Inline to convert -> State WriterState Doc -inlineToConTeXt (Emph lst) = do +inlineToConTeXt (Emph lst) = do contents <- inlineListToConTeXt lst return $ braces $ "\\em " <> contents inlineToConTeXt (Strong lst) = do @@ -273,7 +285,11 @@ inlineToConTeXt (RawInline _ _) = return empty inlineToConTeXt (LineBreak) = return $ text "\\crlf" <> cr inlineToConTeXt Space = return space -- autolink -inlineToConTeXt (Link [Code _ str] (src, tit)) = inlineToConTeXt (Link +inlineToConTeXt (Link [Str str] (src, tit)) + | if "mailto:" `isPrefixOf` src + then src == escapeURI ("mailto:" ++ str) + else src == escapeURI str = + inlineToConTeXt (Link [RawInline "context" "\\hyphenatedurl{", Str str, RawInline "context" "}"] (src, tit)) -- Handle HTML-like internal document references to sections diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 1bcf99dcf..f11338590 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -31,6 +31,7 @@ module Text.Pandoc.Writers.Docbook ( writeDocbook) where import Text.Pandoc.Definition import Text.Pandoc.XML import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Readers.TeXMath import Data.List ( isPrefixOf, intercalate, isSuffixOf ) @@ -47,23 +48,23 @@ authorToDocbook opts name' = let name = render Nothing $ inlinesToDocbook opts name' in if ',' `elem` name then -- last name first - let (lastname, rest) = break (==',') name - firstname = removeLeadingSpace rest in - inTagsSimple "firstname" (text $ escapeStringForXML firstname) <> - inTagsSimple "surname" (text $ escapeStringForXML lastname) + let (lastname, rest) = break (==',') name + firstname = triml rest in + inTagsSimple "firstname" (text $ escapeStringForXML firstname) <> + inTagsSimple "surname" (text $ escapeStringForXML lastname) else -- last name last let namewords = words name - lengthname = length namewords + lengthname = length namewords (firstname, lastname) = case lengthname of - 0 -> ("","") + 0 -> ("","") 1 -> ("", name) n -> (intercalate " " (take (n-1) namewords), last namewords) - in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ - inTagsSimple "surname" (text $ escapeStringForXML lastname) + in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ + inTagsSimple "surname" (text $ escapeStringForXML lastname) -- | Convert Pandoc document to string in Docbook format. writeDocbook :: WriterOptions -> Pandoc -> String -writeDocbook opts (Pandoc (Meta tit auths dat) blocks) = +writeDocbook opts (Pandoc (Meta tit auths dat) blocks) = let title = inlinesToDocbook opts tit authors = map (authorToDocbook opts) auths date = inlinesToDocbook opts dat @@ -73,7 +74,7 @@ writeDocbook opts (Pandoc (Meta tit auths dat) blocks) = else Nothing render' = render colwidth opts' = if "/book>" `isSuffixOf` - (removeTrailingSpace $ writerTemplate opts) + (trimr $ writerTemplate opts) then opts{ writerChapters = True } else opts startLvl = if writerChapters opts' then 0 else 1 @@ -92,7 +93,7 @@ writeDocbook opts (Pandoc (Meta tit auths dat) blocks) = -- | Convert an Element to Docbook. elementToDocbook :: WriterOptions -> Int -> Element -> Doc -elementToDocbook opts _ (Blk block) = blockToDocbook opts block +elementToDocbook opts _ (Blk block) = blockToDocbook opts block elementToDocbook opts lvl (Sec _ _num id' title elements) = -- Docbook doesn't allow sections with no content, so insert some if needed let elements' = if null elements @@ -102,7 +103,7 @@ elementToDocbook opts lvl (Sec _ _num id' title elements) = n | n == 0 -> "chapter" | n >= 1 && n <= 5 -> "sect" ++ show n | otherwise -> "simplesect" - in inTags True tag [("id",id')] $ + in inTags True tag [("id", writerIdentifierPrefix opts ++ id')] $ inTagsSimple "title" (inlinesToDocbook opts title) $$ vcat (map (elementToDocbook opts (lvl + 1)) elements') @@ -115,10 +116,10 @@ plainToPara :: Block -> Block plainToPara (Plain x) = Para x plainToPara x = x --- | Convert a list of pairs of terms and definitions into a list of +-- | Convert a list of pairs of terms and definitions into a list of -- Docbook varlistentrys. deflistItemsToDocbook :: WriterOptions -> [([Inline],[[Block]])] -> Doc -deflistItemsToDocbook opts items = +deflistItemsToDocbook opts items = vcat $ map (\(term, defs) -> deflistItemToDocbook opts term defs) items -- | Convert a term and a list of blocks into a Docbook varlistentry. @@ -141,16 +142,20 @@ listItemToDocbook opts item = -- | Convert a Pandoc block element to Docbook. blockToDocbook :: WriterOptions -> Block -> Doc blockToDocbook _ Null = empty -blockToDocbook _ (Header _ _) = empty -- should not occur after hierarchicalize +blockToDocbook _ (Header _ _ _) = empty -- should not occur after hierarchicalize blockToDocbook opts (Plain lst) = inlinesToDocbook opts lst -blockToDocbook opts (Para [Image txt (src,_)]) = - let capt = inlinesToDocbook opts txt +-- title beginning with fig: indicates that the image is a figure +blockToDocbook opts (Para [Image txt (src,'f':'i':'g':':':_)]) = + let alt = inlinesToDocbook opts txt + capt = if null txt + then empty + else inTagsSimple "title" alt in inTagsIndented "figure" $ - inTagsSimple "title" capt $$ + capt $$ (inTagsIndented "mediaobject" $ (inTagsIndented "imageobject" (selfClosingTag "imagedata" [("fileref",src)])) $$ - inTagsSimple "textobject" (inTagsSimple "phrase" capt)) + inTagsSimple "textobject" (inTagsSimple "phrase" alt)) blockToDocbook opts (Para lst) = inTagsIndented "para" $ inlinesToDocbook opts lst blockToDocbook opts (BlockQuote blocks) = @@ -167,9 +172,9 @@ blockToDocbook _ (CodeBlock (_,classes,_) str) = then [s] else languagesByExtension . map toLower $ s langs = concatMap langsFrom classes -blockToDocbook opts (BulletList lst) = - inTagsIndented "itemizedlist" $ listItemsToDocbook opts lst -blockToDocbook _ (OrderedList _ []) = empty +blockToDocbook opts (BulletList lst) = + inTagsIndented "itemizedlist" $ listItemsToDocbook opts lst +blockToDocbook _ (OrderedList _ []) = empty blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = let attribs = case numstyle of DefaultStyle -> [] @@ -182,12 +187,12 @@ blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = items = if start == 1 then listItemsToDocbook opts (first:rest) else (inTags True "listitem" [("override",show start)] - (blocksToDocbook opts $ map plainToPara first)) $$ - listItemsToDocbook opts rest + (blocksToDocbook opts $ map plainToPara first)) $$ + listItemsToDocbook opts rest in inTags True "orderedlist" attribs items -blockToDocbook opts (DefinitionList lst) = - inTagsIndented "variablelist" $ deflistItemsToDocbook opts lst -blockToDocbook _ (RawBlock "docbook" str) = text str -- raw XML block +blockToDocbook opts (DefinitionList lst) = + inTagsIndented "variablelist" $ deflistItemsToDocbook opts lst +blockToDocbook _ (RawBlock "docbook" str) = text str -- raw XML block -- we allow html for compatibility with earlier versions of pandoc blockToDocbook _ (RawBlock "html" str) = text str -- raw XML block blockToDocbook _ (RawBlock _ _) = empty @@ -237,26 +242,26 @@ inlinesToDocbook opts lst = hcat $ map (inlineToDocbook opts) lst -- | Convert an inline element to Docbook. inlineToDocbook :: WriterOptions -> Inline -> Doc -inlineToDocbook _ (Str str) = text $ escapeStringForXML str -inlineToDocbook opts (Emph lst) = +inlineToDocbook _ (Str str) = text $ escapeStringForXML str +inlineToDocbook opts (Emph lst) = inTagsSimple "emphasis" $ inlinesToDocbook opts lst -inlineToDocbook opts (Strong lst) = +inlineToDocbook opts (Strong lst) = inTags False "emphasis" [("role", "strong")] $ inlinesToDocbook opts lst -inlineToDocbook opts (Strikeout lst) = +inlineToDocbook opts (Strikeout lst) = inTags False "emphasis" [("role", "strikethrough")] $ inlinesToDocbook opts lst -inlineToDocbook opts (Superscript lst) = +inlineToDocbook opts (Superscript lst) = inTagsSimple "superscript" $ inlinesToDocbook opts lst -inlineToDocbook opts (Subscript lst) = +inlineToDocbook opts (Subscript lst) = inTagsSimple "subscript" $ inlinesToDocbook opts lst -inlineToDocbook opts (SmallCaps lst) = +inlineToDocbook opts (SmallCaps lst) = inTags False "emphasis" [("role", "smallcaps")] $ inlinesToDocbook opts lst -inlineToDocbook opts (Quoted _ lst) = +inlineToDocbook opts (Quoted _ lst) = inTagsSimple "quote" $ inlinesToDocbook opts lst inlineToDocbook opts (Cite _ lst) = - inlinesToDocbook opts lst -inlineToDocbook _ (Code _ str) = + inlinesToDocbook opts lst +inlineToDocbook _ (Code _ str) = inTagsSimple "literal" $ text (escapeStringForXML str) inlineToDocbook opts (Math t str) | isMathML (writerHTMLMathMethod opts) = @@ -282,24 +287,24 @@ inlineToDocbook _ Space = space inlineToDocbook opts (Link txt (src, _)) = if isPrefixOf "mailto:" src then let src' = drop 7 src - emailLink = inTagsSimple "email" $ text $ + emailLink = inTagsSimple "email" $ text $ escapeStringForXML $ src' in case txt of - [Code _ s] | s == src' -> emailLink + [Str s] | escapeURI s == src' -> emailLink _ -> inlinesToDocbook opts txt <+> char '(' <> emailLink <> char ')' else (if isPrefixOf "#" src then inTags False "link" [("linkend", drop 1 src)] else inTags False "ulink" [("url", src)]) $ inlinesToDocbook opts txt -inlineToDocbook _ (Image _ (src, tit)) = +inlineToDocbook _ (Image _ (src, tit)) = let titleDoc = if null tit then empty else inTagsIndented "objectinfo" $ inTagsIndented "title" (text $ escapeStringForXML tit) in inTagsIndented "inlinemediaobject" $ inTagsIndented "imageobject" $ titleDoc $$ selfClosingTag "imagedata" [("fileref", src)] -inlineToDocbook opts (Note contents) = +inlineToDocbook opts (Note contents) = inTagsIndented "footnote" $ blocksToDocbook opts contents isMathML :: HTMLMathMethod -> Bool diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 396e7a482..9028acfde 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2012 John MacFarlane <jgm@berkeley.edu> @@ -29,20 +30,17 @@ Conversion of 'Pandoc' documents to docx. -} module Text.Pandoc.Writers.Docx ( writeDocx ) where import Data.List ( intercalate ) -import System.FilePath ( (</>) ) -import qualified Data.ByteString.Lazy as B +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL import qualified Data.Map as M -import Data.ByteString.Lazy.UTF8 ( fromString, toString ) -import Text.Pandoc.UTF8 as UTF8 -import System.IO ( stderr ) +import qualified Text.Pandoc.UTF8 as UTF8 import Codec.Archive.Zip import Data.Time.Clock.POSIX -import Paths_pandoc ( getDataFileName ) import Text.Pandoc.Definition import Text.Pandoc.Generic -import System.Directory import Text.Pandoc.ImageSize import Text.Pandoc.Shared hiding (Element) +import Text.Pandoc.Options import Text.Pandoc.Readers.TeXMath import Text.Pandoc.Highlighting ( highlight ) import Text.Highlighting.Kate.Types () @@ -50,6 +48,10 @@ import Text.XML.Light import Text.TeXMath import Control.Monad.State import Text.Highlighting.Kate +import Data.Unique (hashUnique, newUnique) +import System.Random (randomRIO) +import Text.Printf (printf) +import qualified Control.Exception as E data WriterState = WriterState{ stTextProperties :: [Element] @@ -57,9 +59,9 @@ data WriterState = WriterState{ , stFootnotes :: [Element] , stSectionIds :: [String] , stExternalLinks :: M.Map String String - , stImages :: M.Map FilePath (String, B.ByteString) + , stImages :: M.Map FilePath (String, String, Element, B.ByteString) , stListLevel :: Int - , stListMarker :: ListMarker + , stListNumId :: Int , stNumStyles :: M.Map ListMarker Int , stLists :: [ListMarker] } @@ -78,7 +80,7 @@ defaultWriterState = WriterState{ , stExternalLinks = M.empty , stImages = M.empty , stListLevel = -1 - , stListMarker = NoMarker + , stListNumId = 1 , stNumStyles = M.fromList [(NoMarker, 0)] , stLists = [NoMarker] } @@ -92,68 +94,62 @@ mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) . node (unqual s) +toLazy :: B.ByteString -> BL.ByteString +toLazy = BL.fromChunks . (:[]) + -- | Produce an Docx file from a Pandoc document. -writeDocx :: Maybe FilePath -- ^ Path specified by --reference-docx - -> WriterOptions -- ^ Writer options +writeDocx :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> IO B.ByteString -writeDocx mbRefDocx opts doc@(Pandoc (Meta tit auths date) _) = do + -> IO BL.ByteString +writeDocx opts doc@(Pandoc (Meta tit auths date) _) = do let datadir = writerUserDataDir opts - refArchive <- liftM toArchive $ - case mbRefDocx of - Just f -> B.readFile f - Nothing -> do - let defaultDocx = getDataFileName "reference.docx" >>= B.readFile - case datadir of - Nothing -> defaultDocx - Just d -> do - exists <- doesFileExist (d </> "reference.docx") - if exists - then B.readFile (d </> "reference.docx") - else defaultDocx - - (newContents, st) <- runStateT (writeOpenXML opts{writerWrapText = False} doc) + refArchive <- liftM (toArchive . toLazy) $ + case writerReferenceDocx opts of + Just f -> B.readFile f + Nothing -> readDataFile datadir "reference.docx" + + ((contents, footnotes), st) <- runStateT (writeOpenXML opts{writerWrapText = False} doc) defaultWriterState epochtime <- floor `fmap` getPOSIXTime let imgs = M.elems $ stImages st - let imgPath ident img = "media/" ++ ident ++ - case imageType img of - Just Png -> ".png" - Just Jpeg -> ".jpeg" - Just Gif -> ".gif" - Nothing -> "" - let toImgRel (ident,img) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",imgPath ident img)] () + let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",path)] () let newrels = map toImgRel imgs let relpath = "word/_rels/document.xml.rels" let reldoc = case findEntryByPath relpath refArchive >>= - parseXMLDoc . toString . fromEntry of + parseXMLDoc . UTF8.toStringLazy . fromEntry of Just d -> d Nothing -> error $ relpath ++ "missing in reference docx" let reldoc' = reldoc{ elContent = elContent reldoc ++ map Elem newrels } -- create entries for images - let toImageEntry (ident,img) = toEntry ("word/" ++ imgPath ident img) - epochtime img + let toImageEntry (_,path,_,img) = toEntry ("word/" ++ path) epochtime $ toLazy img let imageEntries = map toImageEntry imgs -- NOW get list of external links and images from this, and do what's needed let toLinkRel (src,ident) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"),("Id",ident),("Target",src),("TargetMode","External") ] () let newrels' = map toLinkRel $ M.toList $ stExternalLinks st let reldoc'' = reldoc' { elContent = elContent reldoc' ++ map Elem newrels' } - let relEntry = toEntry relpath epochtime $ fromString $ showTopElement' reldoc'' - let contentEntry = toEntry "word/document.xml" epochtime $ fromString $ showTopElement' newContents + let relEntry = toEntry relpath epochtime $ UTF8.fromStringLazy $ showTopElement' reldoc'' + let contentEntry = toEntry "word/document.xml" epochtime $ UTF8.fromStringLazy $ showTopElement' contents + -- footnotes + let footnotesEntry = toEntry "word/footnotes.xml" epochtime $ UTF8.fromStringLazy $ + showTopElement' footnotes + -- footnote rels + let footnoteRelEntry = toEntry "word/_rels/footnotes.xml.rels" epochtime $ UTF8.fromStringLazy $ + showTopElement' $ mknode "Relationships" [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")] + $ newrels' -- styles let newstyles = styleToOpenXml $ writerHighlightStyle opts let stylepath = "word/styles.xml" let styledoc = case findEntryByPath stylepath refArchive >>= - parseXMLDoc . toString . fromEntry of + parseXMLDoc . UTF8.toStringLazy . fromEntry of Just d -> d Nothing -> error $ "Unable to parse " ++ stylepath ++ " from reference.docx" let styledoc' = styledoc{ elContent = elContent styledoc ++ map Elem newstyles } - let styleEntry = toEntry stylepath epochtime $ fromString $ showTopElement' styledoc' + let styleEntry = toEntry stylepath epochtime $ UTF8.fromStringLazy $ showTopElement' styledoc' -- construct word/numbering.xml let numpath = "word/numbering.xml" - let numEntry = toEntry numpath epochtime $ fromString $ showTopElement' - $ mkNumbering (stNumStyles st) (stLists st) + numEntry <- (toEntry numpath epochtime . UTF8.fromStringLazy . showTopElement') + `fmap` mkNumbering (stNumStyles st) (stLists st) let docPropsPath = "docProps/core.xml" let docProps = mknode "cp:coreProperties" [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties") @@ -166,18 +162,18 @@ writeDocx mbRefDocx opts doc@(Pandoc (Meta tit auths date) _) = do (maybe "" id $ normalizeDate $ stringify date) : mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] () -- put current time here : map (mknode "dc:creator" [] . stringify) auths - let docPropsEntry = toEntry docPropsPath epochtime $ fromString $ showTopElement' docProps + let docPropsEntry = toEntry docPropsPath epochtime $ UTF8.fromStringLazy $ showTopElement' docProps let relsPath = "_rels/.rels" rels <- case findEntryByPath relsPath refArchive of - Just e -> return $ toString $ fromEntry e + Just e -> return $ UTF8.toStringLazy $ fromEntry e Nothing -> err 57 "could not find .rels/_rels in reference docx" -- fix .rels/_rels, which can get screwed up when reference.docx is edited by Word let rels' = substitute "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties" rels - let relsEntry = toEntry relsPath epochtime $ fromString rels' + let relsEntry = toEntry relsPath epochtime $ UTF8.fromStringLazy rels' let archive = foldr addEntryToArchive refArchive $ - relsEntry : contentEntry : relEntry : numEntry : styleEntry : docPropsEntry : imageEntries + relsEntry : contentEntry : relEntry : footnoteRelEntry : numEntry : styleEntry : footnotesEntry : docPropsEntry : imageEntries return $ fromArchive archive styleToOpenXml :: Style -> [Element] @@ -215,11 +211,12 @@ styleToOpenXml style = parStyle : map toStyle alltoktypes $ backgroundColor style ) ] -mkNumbering :: M.Map ListMarker Int -> [ListMarker] -> Element -mkNumbering markers lists = - mknode "w:numbering" [("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main")] - $ map mkAbstractNum (M.toList markers) - ++ zipWith (mkNum markers) lists [1..(length lists)] +mkNumbering :: M.Map ListMarker Int -> [ListMarker] -> IO Element +mkNumbering markers lists = do + elts <- mapM mkAbstractNum (M.toList markers) + return $ mknode "w:numbering" + [("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main")] + $ elts ++ zipWith (mkNum markers) lists [1..(length lists)] mkNum :: M.Map ListMarker Int -> ListMarker -> Int -> Element mkNum markers marker numid = @@ -233,10 +230,12 @@ mkNum markers marker numid = $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6] where absnumid = maybe 0 id $ M.lookup marker markers -mkAbstractNum :: (ListMarker,Int) -> Element -mkAbstractNum (marker,numid) = - mknode "w:abstractNum" [("w:abstractNumId",show numid)] - $ mknode "w:multiLevelType" [("w:val","multilevel")] () +mkAbstractNum :: (ListMarker,Int) -> IO Element +mkAbstractNum (marker,numid) = do + nsid <- randomRIO (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) + return $ mknode "w:abstractNum" [("w:abstractNumId",show numid)] + $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] () + : mknode "w:multiLevelType" [("w:val","multilevel")] () : map (mkLvl marker) [0..6] mkLvl :: ListMarker -> Int -> Element @@ -285,8 +284,11 @@ mkLvl marker lvl = patternFor TwoParens s = "(" ++ s ++ ")" patternFor _ s = s ++ "." --- | Convert Pandoc document to string in OpenXML format. -writeOpenXML :: WriterOptions -> Pandoc -> WS Element +getNumId :: WS Int +getNumId = length `fmap` gets stLists + +-- | Convert Pandoc document to two OpenXML elements (the main document and footnotes). +writeOpenXML :: WriterOptions -> Pandoc -> WS (Element, Element) writeOpenXML opts (Pandoc (Meta tit auths dat) blocks) = do title <- withParaProp (pStyle "Title") $ blocksToOpenXML opts [Para tit | not (null tit)] authors <- withParaProp (pStyle "Authors") $ blocksToOpenXML opts @@ -296,13 +298,10 @@ writeOpenXML opts (Pandoc (Meta tit auths dat) blocks) = do convertSpace (Str x : Str y : xs) = Str (x ++ y) : xs convertSpace xs = xs let blocks' = bottomUp convertSpace $ blocks - doc <- blocksToOpenXML opts blocks' + doc' <- blocksToOpenXML opts blocks' notes' <- reverse `fmap` gets stFootnotes - let notes = case notes' of - [] -> [] - ns -> [mknode "w:footnotes" [] ns] let meta = title ++ authors ++ date - return $ mknode "w:document" + let stdAttributes = [("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main") ,("xmlns:m","http://schemas.openxmlformats.org/officeDocument/2006/math") ,("xmlns:r","http://schemas.openxmlformats.org/officeDocument/2006/relationships") @@ -312,7 +311,9 @@ writeOpenXML opts (Pandoc (Meta tit auths dat) blocks) = do ,("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")] - $ mknode "w:body" [] (meta ++ doc ++ notes) + let doc = mknode "w:document" stdAttributes $ mknode "w:body" [] (meta ++ doc') + let notes = mknode "w:footnotes" stdAttributes notes' + return (doc, notes) -- | Convert a list of Pandoc blocks to OpenXML. blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element] @@ -324,23 +325,32 @@ pStyle sty = mknode "w:pStyle" [("w:val",sty)] () rStyle :: String -> Element rStyle sty = mknode "w:rStyle" [("w:val",sty)] () +getUniqueId :: MonadIO m => m String +-- the + 20 is to ensure that there are no clashes with the rIds +-- already in word/document.xml.rel +getUniqueId = liftIO $ (show . (+ 20) . hashUnique) `fmap` newUnique + -- | Convert a Pandoc block element to OpenXML. blockToOpenXML :: WriterOptions -> Block -> WS [Element] blockToOpenXML _ Null = return [] -blockToOpenXML opts (Header lev lst) = do +blockToOpenXML opts (Header lev (ident,_,_) lst) = do contents <- withParaProp (pStyle $ "Heading" ++ show lev) $ blockToOpenXML opts (Para lst) usedIdents <- gets stSectionIds - let ident = uniqueIdent lst usedIdents - modify $ \s -> s{ stSectionIds = ident : stSectionIds s } - let bookmarkStart = mknode "w:bookmarkStart" [("w:id",ident) - ,("w:name",ident)] () - let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id",ident)] () + let bookmarkName = if null ident + then uniqueIdent lst usedIdents + else ident + modify $ \s -> s{ stSectionIds = bookmarkName : stSectionIds s } + id' <- getUniqueId + let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id') + ,("w:name",bookmarkName)] () + let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] () return $ [bookmarkStart] ++ contents ++ [bookmarkEnd] blockToOpenXML opts (Plain lst) = blockToOpenXML opts (Para lst) -blockToOpenXML opts (Para x@[Image alt _]) = do +-- title beginning with fig: indicates that the image is a figure +blockToOpenXML opts (Para [Image alt (src,'f':'i':'g':':':tit)]) = do paraProps <- getParaProps - contents <- inlinesToOpenXML opts x + contents <- inlinesToOpenXML opts [Image alt (src,tit)] captionNode <- withParaProp (pStyle "ImageCaption") $ blockToOpenXML opts (Para alt) return $ mknode "w:p" [] (paraProps ++ contents) : captionNode @@ -402,11 +412,13 @@ blockToOpenXML opts (Table caption aligns widths headers rows) = do blockToOpenXML opts (BulletList lst) = do let marker = BulletMarker addList marker - asList $ concat `fmap` mapM (listItemToOpenXML opts marker) lst + numid <- getNumId + asList $ concat `fmap` mapM (listItemToOpenXML opts numid) lst blockToOpenXML opts (OrderedList (start, numstyle, numdelim) lst) = do let marker = NumberMarker numstyle numdelim start addList marker - asList $ concat `fmap` mapM (listItemToOpenXML opts marker) lst + numid <- getNumId + asList $ concat `fmap` mapM (listItemToOpenXML opts numid) lst blockToOpenXML opts (DefinitionList items) = concat `fmap` mapM (definitionListItemToOpenXML opts) items @@ -418,9 +430,6 @@ definitionListItemToOpenXML opts (term,defs) = do $ concat `fmap` mapM (blocksToOpenXML opts) defs return $ term' ++ defs' -getNumId :: WS Int -getNumId = length `fmap` gets stLists - addList :: ListMarker -> WS () addList marker = do lists <- gets stLists @@ -431,11 +440,11 @@ addList marker = do Nothing -> modify $ \st -> st{ stNumStyles = M.insert marker (M.size numStyles + 1) numStyles } -listItemToOpenXML :: WriterOptions -> ListMarker -> [Block] -> WS [Element] +listItemToOpenXML :: WriterOptions -> Int -> [Block] -> WS [Element] listItemToOpenXML _ _ [] = return [] -listItemToOpenXML opts marker (first:rest) = do - first' <- withMarker marker $ blockToOpenXML opts first - rest' <- withMarker NoMarker $ blocksToOpenXML opts rest +listItemToOpenXML opts numid (first:rest) = do + first' <- withNumId numid $ blockToOpenXML opts first + rest' <- withNumId 1 $ blocksToOpenXML opts rest return $ first' ++ rest' alignmentToString :: Alignment -> [Char] @@ -449,12 +458,12 @@ alignmentToString alignment = case alignment of inlinesToOpenXML :: WriterOptions -> [Inline] -> WS [Element] inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst -withMarker :: ListMarker -> WS a -> WS a -withMarker m p = do - origMarker <- gets stListMarker - modify $ \st -> st{ stListMarker = m } +withNumId :: Int -> WS a -> WS a +withNumId numid p = do + origNumId <- gets stListNumId + modify $ \st -> st{ stListNumId = numid } result <- p - modify $ \st -> st{ stListMarker = origMarker } + modify $ \st -> st{ stListNumId = origNumId } return result asList :: WS a -> WS a @@ -489,10 +498,7 @@ getParaProps :: WS [Element] getParaProps = do props <- gets stParaProperties listLevel <- gets stListLevel - listMarker <- gets stListMarker - numid <- case listMarker of - NoMarker -> return 1 - _ -> getNumId + numid <- gets stListNumId let listPr = if listLevel >= 0 then [ mknode "w:numPr" [] [ mknode "w:numId" [("w:val",show numid)] () @@ -543,7 +549,7 @@ inlineToOpenXML opts (SmallCaps lst) = inlineToOpenXML opts (Strikeout lst) = withTextProp (mknode "w:strike" [] ()) $ inlinesToOpenXML opts lst -inlineToOpenXML _ LineBreak = return [ mknode "w:br" [] () ] +inlineToOpenXML _ LineBreak = return [br] inlineToOpenXML _ (RawInline f str) | f == "openxml" = return [ x | Elem x <- parseXML str ] | otherwise = return [] @@ -562,23 +568,21 @@ inlineToOpenXML opts (Math DisplayMath str) = Left _ -> do fallback <- inlinesToOpenXML opts (readTeXMath str) return $ [br] ++ fallback ++ [br] - where br = mknode "w:br" [] () inlineToOpenXML opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML _ (Code attrs str) = withTextProp (rStyle "VerbatimChar") $ case highlight formatOpenXML attrs str of - Nothing -> intercalate [mknode "w:br" [] ()] + Nothing -> intercalate [br] `fmap` (mapM formattedString $ lines str) Just h -> return h - where formatOpenXML _fmtOpts = intercalate [mknode "w:br" [] ()] . - map (map toHlTok) + where formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok) toHlTok (toktype,tok) = mknode "w:r" [] [ mknode "w:rPr" [] [ rStyle $ show toktype ] , mknode "w:t" [("xml:space","preserve")] tok ] inlineToOpenXML opts (Note bs) = do notes <- gets stFootnotes - let notenum = length notes + 1 + notenum <- getUniqueId let notemarker = mknode "w:r" [] [ mknode "w:rPr" [] (rStyle "FootnoteReference") , mknode "w:footnoteRef" [] () ] @@ -594,11 +598,11 @@ inlineToOpenXML opts (Note bs) = do $ insertNoteRef bs modify $ \st -> st{ stListLevel = oldListLevel, stParaProperties = oldParaProperties, stTextProperties = oldTextProperties } - let newnote = mknode "w:footnote" [("w:id",show notenum)] $ contents + let newnote = mknode "w:footnote" [("w:id", notenum)] $ contents modify $ \s -> s{ stFootnotes = newnote : notes } return [ mknode "w:r" [] [ mknode "w:rPr" [] (rStyle "FootnoteReference") - , mknode "w:footnoteReference" [("w:id", show notenum)] () ] ] + , mknode "w:footnoteReference" [("w:id", notenum)] () ] ] -- internal link: inlineToOpenXML opts (Link txt ('#':xs,_)) = do contents <- withTextProp (rStyle "Hyperlink") $ inlinesToOpenXML opts txt @@ -607,65 +611,76 @@ inlineToOpenXML opts (Link txt ('#':xs,_)) = do inlineToOpenXML opts (Link txt (src,_)) = do contents <- withTextProp (rStyle "Hyperlink") $ inlinesToOpenXML opts txt extlinks <- gets stExternalLinks - ind <- case M.lookup src extlinks of + id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do - let i = "link" ++ show (M.size extlinks) + i <- ("rId"++) `fmap` getUniqueId modify $ \st -> st{ stExternalLinks = M.insert src i extlinks } return i - return [ mknode "w:hyperlink" [("r:id",ind)] contents ] + return [ mknode "w:hyperlink" [("r:id",id')] contents ] inlineToOpenXML opts (Image alt (src, tit)) = do - exists <- liftIO $ doesFileExist src - if exists - then do - imgs <- gets stImages - (ident,size) <- case M.lookup src imgs of - Just (i,img) -> return (i, imageSize img) - Nothing -> do - img <- liftIO $ B.readFile src - let ident' = "image" ++ show (M.size imgs + 1) - let size' = imageSize img - modify $ \st -> st{ - stImages = M.insert src (ident',img) $ stImages st } - return (ident',size') - let (xpt,ypt) = maybe (120,120) sizeInPoints size - -- 12700 emu = 1 pt - let (xemu,yemu) = (xpt * 12700, ypt * 12700) - let cNvPicPr = mknode "pic:cNvPicPr" [] $ - mknode "a:picLocks" [("noChangeArrowheads","1"),("noChangeAspect","1")] () - let nvPicPr = mknode "pic:nvPicPr" [] - [ mknode "pic:cNvPr" - [("descr",src),("id","0"),("name","Picture")] () - , cNvPicPr ] - let blipFill = mknode "pic:blipFill" [] - [ mknode "a:blip" [("r:embed",ident)] () - , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] - let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x","0"),("y","0")] () - , mknode "a:ext" [("cx",show xemu),("cy",show yemu)] () ] - 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 "pic:spPr" [("bwMode","auto")] - [xfrm, prstGeom, mknode "a:noFill" [] (), ln] - let graphic = mknode "a:graphic" [] $ - mknode "a:graphicData" [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] - [ mknode "pic:pic" [] - [ nvPicPr - , blipFill - , spPr ] ] - return [ mknode "w:r" [] $ - mknode "w:drawing" [] $ - mknode "wp:inline" [] - [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () - , mknode "wp:effectExtent" [("b","0"),("l","0"),("r","0"),("t","0")] () - , mknode "wp:docPr" [("descr",tit),("id","1"),("name","Picture")] () - , graphic ] ] - else do - liftIO $ UTF8.hPutStrLn stderr $ - "Could not find image `" ++ src ++ "', skipping..." - inlinesToOpenXML opts alt + -- first, check to see if we've already done this image + imgs <- gets stImages + case M.lookup src imgs of + Just (_,_,elt,_) -> return [elt] + Nothing -> do + let sourceDir = writerSourceDirectory opts + res <- liftIO $ E.try $ fetchItem sourceDir src + case res of + Left (_ :: E.SomeException) -> do + liftIO $ warn $ "Could not find image `" ++ src ++ "', skipping..." + -- emit alt text + inlinesToOpenXML opts alt + Right (img, _) -> do + ident <- ("rId"++) `fmap` getUniqueId + let size = imageSize img + let (xpt,ypt) = maybe (120,120) sizeInPoints size + -- 12700 emu = 1 pt + let (xemu,yemu) = (xpt * 12700, ypt * 12700) + let cNvPicPr = mknode "pic:cNvPicPr" [] $ + mknode "a:picLocks" [("noChangeArrowheads","1"),("noChangeAspect","1")] () + let nvPicPr = mknode "pic:nvPicPr" [] + [ mknode "pic:cNvPr" + [("descr",src),("id","0"),("name","Picture")] () + , cNvPicPr ] + let blipFill = mknode "pic:blipFill" [] + [ mknode "a:blip" [("r:embed",ident)] () + , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] + let xfrm = mknode "a:xfrm" [] + [ mknode "a:off" [("x","0"),("y","0")] () + , mknode "a:ext" [("cx",show xemu),("cy",show yemu)] () ] + 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 "pic:spPr" [("bwMode","auto")] + [xfrm, prstGeom, mknode "a:noFill" [] (), ln] + let graphic = mknode "a:graphic" [] $ + mknode "a:graphicData" [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] + [ mknode "pic:pic" [] + [ nvPicPr + , blipFill + , spPr ] ] + let imgElt = mknode "w:r" [] $ + mknode "w:drawing" [] $ + mknode "wp:inline" [] + [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () + , mknode "wp:effectExtent" [("b","0"),("l","0"),("r","0"),("t","0")] () + , mknode "wp:docPr" [("descr",tit),("id","1"),("name","Picture")] () + , graphic ] + modify $ \st -> st{ stImages = M.insert src (ident, imgPath ident img, imgElt, img) $ stImages st } + return [imgElt] + +imgPath :: String -> B.ByteString -> String +imgPath ident img = "media/" ++ ident ++ + case imageType img of + Just Png -> ".png" + Just Jpeg -> ".jpeg" + Just Gif -> ".gif" + Nothing -> "" + +br :: Element +br = mknode "w:r" [] [mknode "w:cr" [] () ] diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d1cd67c68..4155cca05 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -30,14 +30,20 @@ Conversion of 'Pandoc' documents to EPUB. module Text.Pandoc.Writers.EPUB ( writeEPUB ) where import Data.IORef import Data.Maybe ( fromMaybe, isNothing ) -import Data.List ( findIndices, isPrefixOf ) +import Data.List ( isInfixOf, intercalate ) import System.Environment ( getEnv ) -import System.FilePath ( (</>), (<.>), takeBaseName, takeExtension, takeFileName ) +import Text.Printf (printf) +import System.FilePath ( (</>), takeBaseName, takeExtension, takeFileName ) import qualified Data.ByteString.Lazy as B -import Data.ByteString.Lazy.UTF8 ( fromString ) +import qualified Data.ByteString.Lazy.Char8 as B8 +import Text.Pandoc.UTF8 ( fromStringLazy, toString ) import Codec.Archive.Zip import Data.Time.Clock.POSIX +import Data.Time +import System.Locale import Text.Pandoc.Shared hiding ( Element ) +import qualified Text.Pandoc.Shared as Shared +import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Generic import Control.Monad.State @@ -48,86 +54,105 @@ import Text.Pandoc.Writers.Markdown ( writePlain ) import Data.Char ( toLower ) import Network.URI ( unEscapeString ) import Text.Pandoc.MIME (getMimeType) -#if ! MIN_VERSION_base(4,6,0) import Prelude hiding (catch) -#endif import Control.Exception (catch, SomeException) +import Text.Blaze.Html.Renderer.Utf8 (renderHtml) -- | Produce an EPUB file from a Pandoc document. -writeEPUB :: Maybe String -- ^ EPUB stylesheet specified at command line - -> [FilePath] -- ^ Paths to fonts to embed - -> WriterOptions -- ^ Writer options +writeEPUB :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO B.ByteString -writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do +writeEPUB opts doc@(Pandoc meta _) = do + let version = maybe EPUB2 id (writerEpubVersion opts) + let epub3 = version == EPUB3 epochtime <- floor `fmap` getPOSIXTime let mkEntry path content = toEntry path epochtime content + let vars = ("epub3", if epub3 then "true" else "false") + : ("css", "stylesheet.css") + : writerVariables opts let opts' = opts{ writerEmailObfuscation = NoObfuscation , writerStandalone = True + , writerSectionDivs = True + , writerHtml5 = epub3 + , writerTableOfContents = False -- we always have one in epub + , writerVariables = vars + , writerHTMLMathMethod = + if epub3 + then MathML Nothing + else writerHTMLMathMethod opts , writerWrapText = False } let sourceDir = writerSourceDirectory opts' - let vars = writerVariables opts' let mbCoverImage = lookup "epub-cover-image" vars - titlePageTemplate <- readDataFile (writerUserDataDir opts) - $ "templates" </> "epub-titlepage" <.> "html" - - coverImageTemplate <- readDataFile (writerUserDataDir opts) - $ "templates" </> "epub-coverimage" <.> "html" - - pageTemplate <- readDataFile (writerUserDataDir opts) - $ "templates" </> "epub-page" <.> "html" - -- cover page (cpgEntry, cpicEntry) <- case mbCoverImage of Nothing -> return ([],[]) Just img -> do let coverImage = "cover-image" ++ takeExtension img - let cpContent = fromString $ writeHtmlString - opts'{writerTemplate = coverImageTemplate, - writerVariables = ("coverimage",coverImage):vars} - (Pandoc meta []) + let cpContent = renderHtml $ writeHtml opts' + (Pandoc meta [RawBlock "html" $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) imgContent <- B.readFile img return ( [mkEntry "cover.xhtml" cpContent] , [mkEntry coverImage imgContent] ) -- title page - let tpContent = fromString $ writeHtmlString - opts'{writerTemplate = titlePageTemplate} - (Pandoc meta []) + let tpContent = renderHtml $ writeHtml opts'{ + writerVariables = ("titlepage","true"):vars } + (Pandoc meta []) let tpEntry = mkEntry "title_page.xhtml" tpContent -- handle pictures picsRef <- newIORef [] Pandoc _ blocks <- bottomUpM - (transformInlines (writerHTMLMathMethod opts) sourceDir picsRef) doc + (transformInlines (writerHTMLMathMethod opts') sourceDir picsRef) doc pics <- readIORef picsRef - let readPicEntry (oldsrc, newsrc) = readEntry [] oldsrc >>= \e -> - return e{ eRelativePath = newsrc } + let readPicEntry (oldsrc, newsrc) = do + (img,_) <- fetchItem sourceDir oldsrc + return $ toEntry newsrc epochtime $ B.fromChunks . (:[]) $ img picEntries <- mapM readPicEntry pics -- handle fonts let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f - fontEntries <- mapM mkFontEntry fonts + fontEntries <- mapM mkFontEntry $ writerEpubFonts opts' -- body pages - let isH1 (Header 1 _) = True - isH1 _ = False + + -- add level 1 header to beginning if none there + let blocks' = addIdentifiers + $ case blocks of + (Header 1 _ _ : _) -> blocks + _ -> Header 1 ("",[],[]) (docTitle meta) : blocks + + let chapterHeaderLevel = writerEpubChapterLevel opts -- internal reference IDs change when we chunk the file, - -- so the next two lines fix that: - let reftable = correlateRefs blocks - let blocks' = replaceRefs reftable blocks - let h1Indices = dropWhile (== 0) $ findIndices isH1 blocks' - let chunks = splitByIndices h1Indices blocks' - let titleize (Header 1 xs : ys) = Pandoc meta{docTitle = xs} ys - titleize xs = Pandoc meta xs - let chapters = map titleize chunks - let chapToHtml = writeHtmlString opts'{ writerTemplate = pageTemplate } - let chapterToEntry :: Int -> Pandoc -> Entry - chapterToEntry num chap = mkEntry ("ch" ++ show num ++ ".xhtml") $ - fromString $ chapToHtml chap - let chapterEntries = zipWith chapterToEntry [1..] chapters + -- so that '#my-header-1' might turn into 'chap004.xhtml#my-header'. + -- the next two lines fix that: + let reftable = correlateRefs chapterHeaderLevel blocks' + let blocks'' = replaceRefs reftable blocks' + + let isChapterHeader (Header n _ _) = n <= chapterHeaderLevel + isChapterHeader _ = False + + let toChunks :: [Block] -> [[Block]] + toChunks [] = [] + toChunks (b:bs) = (b:xs) : toChunks ys + where (xs,ys) = break isChapterHeader bs + + let chunks = toChunks blocks'' + + let chapToEntry :: Int -> [Block] -> Entry + chapToEntry num bs = mkEntry (showChapter num) + $ renderHtml + $ writeHtml opts' + $ case bs of + (Header _ _ xs : _) -> Pandoc (Meta xs [] []) bs + _ -> Pandoc (Meta [] [] []) bs + + let chapterEntries = zipWith chapToEntry [1..] chunks + + -- incredibly inefficient (TODO): + let containsMathML ent = "<math" `isInfixOf` (B8.unpack $ fromEntry ent) -- contents.opf localeLang <- catch (liftM (map (\c -> if c == '_' then '-' else c) . @@ -138,9 +163,11 @@ writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do Nothing -> localeLang uuid <- getRandomUUID let chapterNode ent = unode "item" ! - [("id", takeBaseName $ eRelativePath ent), - ("href", eRelativePath ent), - ("media-type", "application/xhtml+xml")] $ () + ([("id", takeBaseName $ eRelativePath ent), + ("href", eRelativePath ent), + ("media-type", "application/xhtml+xml")] + ++ [("properties","mathml") | epub3 && + containsMathML ent]) $ () let chapterRefNode ent = unode "itemref" ! [("idref", takeBaseName $ eRelativePath ent)] $ () let pictureNode ent = unode "item" ! @@ -152,24 +179,34 @@ writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do [("id", takeBaseName $ eRelativePath ent), ("href", eRelativePath ent), ("media-type", maybe "" id $ getMimeType $ eRelativePath ent)] $ () - let plainify t = removeTrailingSpace $ + let plainify t = trimr $ writePlain opts'{ writerStandalone = False } $ Pandoc meta [Plain t] let plainTitle = plainify $ docTitle meta let plainAuthors = map plainify $ docAuthors meta - let plainDate = maybe "" id $ normalizeDate $ stringify $ docDate meta - let contentsData = fromString $ ppTopElement $ - unode "package" ! [("version","2.0") + currentTime <- getCurrentTime + let plainDate = maybe (showDateTimeISO8601 currentTime) id + $ normalizeDate $ stringify $ docDate meta + let contentsData = fromStringLazy $ ppTopElement $ + unode "package" ! [("version", case version of + EPUB2 -> "2.0" + EPUB3 -> "3.0") ,("xmlns","http://www.idpf.org/2007/opf") ,("unique-identifier","BookId")] $ - [ metadataElement (writerEPUBMetadata opts') - uuid lang plainTitle plainAuthors plainDate mbCoverImage + [ metadataElement version (writerEpubMetadata opts') + uuid lang plainTitle plainAuthors plainDate currentTime mbCoverImage , unode "manifest" $ [ unode "item" ! [("id","ncx"), ("href","toc.ncx") ,("media-type","application/x-dtbncx+xml")] $ () , unode "item" ! [("id","style"), ("href","stylesheet.css") ,("media-type","text/css")] $ () ] ++ + [ unode "item" ! [("id","nav") + ,("href","nav.xhtml") + ,("properties","nav") + ,("media-type","application/xhtml+xml")] $ () + | version == EPUB3 + ] ++ map chapterNode (cpgEntry ++ (tpEntry : chapterEntries)) ++ map pictureNode (cpicEntry ++ picEntries) ++ map fontNode fontEntries @@ -183,14 +220,44 @@ writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do let contentsEntry = mkEntry "content.opf" contentsData -- toc.ncx - let navPointNode ent n tit = unode "navPoint" ! - [("id", "navPoint-" ++ show n) - ,("playOrder", show n)] $ - [ unode "navLabel" $ unode "text" tit - , unode "content" ! [("src", - eRelativePath ent)] $ () - ] - let tocData = fromString $ ppTopElement $ + let secs = hierarchicalize blocks'' + + let tocLevel = writerTOCDepth opts + + let navPointNode :: (Int -> String -> String -> [Element] -> Element) + -> Shared.Element -> State Int Element + navPointNode formatter (Sec _ nums ident ils children) = do + n <- get + modify (+1) + let showNums :: [Int] -> String + showNums = intercalate "." . map show + let tit' = plainify ils + let tit = if writerNumberSections opts + then showNums nums ++ " " ++ tit' + else tit' + let src = case lookup ident reftable of + Just x -> x + Nothing -> error (ident ++ " not found in reftable") + let isSec (Sec lev _ _ _ _) = lev <= tocLevel + isSec _ = False + let subsecs = filter isSec children + subs <- mapM (navPointNode formatter) subsecs + return $ formatter n tit src subs + navPointNode _ (Blk _) = error "navPointNode encountered Blk" + + let navMapFormatter :: Int -> String -> String -> [Element] -> Element + navMapFormatter n tit src subs = unode "navPoint" ! + [("id", "navPoint-" ++ show n) + ,("playOrder", show n)] $ + [ unode "navLabel" $ unode "text" tit + , unode "content" ! [("src", src)] $ () + ] ++ subs + + let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ + [ unode "navLabel" $ unode "text" (plainify $ docTitle meta) + , unode "content" ! [("src","title_page.xhtml")] $ () ] + + let tocData = fromStringLazy $ ppTopElement $ unode "ncx" ! [("version","2005-1") ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $ [ unode "head" $ @@ -207,18 +274,36 @@ writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do Just _ -> [unode "meta" ! [("name","cover"), ("content","cover-image")] $ ()] , unode "docTitle" $ unode "text" $ plainTitle - , unode "navMap" $ zipWith3 navPointNode (tpEntry : chapterEntries) - [1..(length chapterEntries + 1)] - ("Title Page" : map (\(Pandoc m _) -> - plainify $ docTitle m) chapters) + , unode "navMap" $ + tpNode : evalState (mapM (navPointNode navMapFormatter) secs) 1 ] let tocEntry = mkEntry "toc.ncx" tocData + let navXhtmlFormatter :: Int -> String -> String -> [Element] -> Element + navXhtmlFormatter n tit src subs = unode "li" ! + [("id", "toc-li-" ++ show n)] $ + (unode "a" ! [("href",src)] + $ (unode "span" tit)) + : case subs of + [] -> [] + (_:_) -> [unode "ol" subs] + + let navData = fromStringLazy $ ppTopElement $ + unode "html" ! [("xmlns","http://www.w3.org/1999/xhtml") + ,("xmlns:epub","http://www.idpf.org/2007/ops")] $ + [ unode "head" $ unode "title" plainTitle + , unode "body" $ + unode "nav" ! [("epub:type","toc")] $ + [ unode "h1" plainTitle + , unode "ol" $ evalState (mapM (navPointNode navXhtmlFormatter) secs) 1] + ] + let navEntry = mkEntry "nav.xhtml" navData + -- mimetype - let mimetypeEntry = mkEntry "mimetype" $ fromString "application/epub+zip" + let mimetypeEntry = mkEntry "mimetype" $ fromStringLazy "application/epub+zip" -- container.xml - let containerData = fromString $ ppTopElement $ + let containerData = fromStringLazy $ ppTopElement $ unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ @@ -227,54 +312,65 @@ writeEPUB mbStylesheet fonts opts doc@(Pandoc meta _) = do let containerEntry = mkEntry "META-INF/container.xml" containerData -- com.apple.ibooks.display-options.xml - let apple = fromString $ ppTopElement $ + let apple = fromStringLazy $ ppTopElement $ unode "display_options" $ unode "platform" ! [("name","*")] $ unode "option" ! [("name","specified-fonts")] $ "true" let appleEntry = mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple -- stylesheet - stylesheet <- case mbStylesheet of + stylesheet <- case writerEpubStylesheet opts of Just s -> return s - Nothing -> readDataFile (writerUserDataDir opts) "epub.css" - let stylesheetEntry = mkEntry "stylesheet.css" $ fromString stylesheet + Nothing -> toString `fmap` + readDataFile (writerUserDataDir opts) "epub.css" + let stylesheetEntry = mkEntry "stylesheet.css" $ fromStringLazy stylesheet -- construct archive let archive = foldr addEntryToArchive emptyArchive (mimetypeEntry : containerEntry : appleEntry : stylesheetEntry : tpEntry : contentsEntry : tocEntry : - (picEntries ++ cpicEntry ++ cpgEntry ++ chapterEntries ++ fontEntries) ) + ([navEntry | version == EPUB3] ++ picEntries ++ cpicEntry ++ cpgEntry ++ + chapterEntries ++ fontEntries) ) return $ fromArchive archive -metadataElement :: String -> UUID -> String -> String -> [String] -> String -> Maybe a -> Element -metadataElement metadataXML uuid lang title authors date mbCoverImage = +metadataElement :: EPUBVersion -> String -> UUID -> String -> String -> [String] + -> String -> UTCTime -> Maybe a -> Element +metadataElement version metadataXML uuid lang title authors date currentTime mbCoverImage = let userNodes = parseXML metadataXML elt = unode "metadata" ! [("xmlns:dc","http://purl.org/dc/elements/1.1/") ,("xmlns:opf","http://www.idpf.org/2007/opf")] $ - filter isDublinCoreElement $ onlyElems userNodes + filter isMetadataElement $ onlyElems userNodes dublinElements = ["contributor","coverage","creator","date", "description","format","identifier","language","publisher", "relation","rights","source","subject","title","type"] - isDublinCoreElement e = qPrefix (elName e) == Just "dc" && - qName (elName e) `elem` dublinElements + isMetadataElement e = (qPrefix (elName e) == Just "dc" && + qName (elName e) `elem` dublinElements) || + (qPrefix (elName e) == Nothing && + qName (elName e) `elem` ["link","meta"]) contains e n = not (null (findElements (QName n Nothing (Just "dc")) e)) newNodes = [ unode "dc:title" title | not (elt `contains` "title") ] ++ [ unode "dc:language" lang | not (elt `contains` "language") ] ++ [ unode "dc:identifier" ! [("id","BookId")] $ show uuid | not (elt `contains` "identifier") ] ++ - [ unode "dc:creator" ! [("opf:role","aut")] $ a | a <- authors ] ++ + [ unode "dc:creator" ! [("opf:role","aut") | version == EPUB2] + $ a | a <- authors ] ++ [ unode "dc:date" date | not (elt `contains` "date") ] ++ + [ unode "meta" ! [("property", "dcterms:modified")] $ + (showDateTimeISO8601 currentTime) | version == EPUB3 ] ++ [ unode "meta" ! [("name","cover"), ("content","cover-image")] $ () | not (isNothing mbCoverImage) ] in elt{ elContent = elContent elt ++ map Elem newNodes } +showDateTimeISO8601 :: UTCTime -> String +showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" + transformInlines :: HTMLMathMethod -> FilePath -> IORef [(FilePath, FilePath)] -- ^ (oldpath, newpath) images -> [Inline] -> IO [Inline] -transformInlines _ _ _ (Image lab (src,_) : xs) | isNothing (imageTypeOf src) = - return $ Emph lab : xs +transformInlines _ _ _ (Image lab (src,_) : xs) + | isNothing (imageTypeOf src) = return $ Emph lab : xs transformInlines _ sourceDir picsRef (Image lab (src,tit) : xs) = do let src' = unEscapeString src pics <- readIORef picsRef @@ -288,17 +384,12 @@ transformInlines _ sourceDir picsRef (Image lab (src,tit) : xs) = do return new return $ Image lab (newsrc, tit) : xs transformInlines (MathML _) _ _ (x@(Math _ _) : xs) = do - let writeHtmlInline opts z = removeTrailingSpace $ + -- note: ideally we'd use a switch statement to provide a fallback + -- but switch does not seem to be widely implemented yet, so we just + -- provide the mathml + let writeHtmlInline opts z = trimr $ writeHtmlString opts $ Pandoc (Meta [] [] []) [Plain [z]] - mathml = writeHtmlInline defaultWriterOptions{ - writerHTMLMathMethod = MathML Nothing } x - fallback = writeHtmlInline defaultWriterOptions{ - writerHTMLMathMethod = PlainMath } x - inOps = "<ops:switch xmlns:ops=\"http://www.idpf.org/2007/ops\">" ++ - "<ops:case required-namespace=\"http://www.w3.org/1998/Math/MathML\">" ++ - mathml ++ "</ops:case><ops:default>" ++ fallback ++ "</ops:default>" ++ - "</ops:switch>" - result = if "<math" `isPrefixOf` mathml then inOps else mathml + result = writeHtmlInline def{writerHTMLMathMethod = MathML Nothing } x return $ RawInline "html" result : xs transformInlines _ _ _ xs = return xs @@ -314,9 +405,9 @@ ppTopElement = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ++) . unEntity . unEntity ('&':'#':xs) = let (ds,ys) = break (==';') xs rest = drop 1 ys - in case reads ('\'':'\\':ds ++ "'") of - ((x,_):_) -> x : unEntity rest - _ -> '&':'#':unEntity xs + in case safeRead ('\'':'\\':ds ++ "'") of + Just x -> x : unEntity rest + Nothing -> '&':'#':unEntity xs unEntity (x:xs) = x : unEntity xs imageTypeOf :: FilePath -> Maybe String @@ -332,38 +423,45 @@ imageTypeOf x = case drop 1 (map toLower (takeExtension x)) of data IdentState = IdentState{ chapterNumber :: Int, - runningIdents :: [String], - chapterIdents :: [String], identTable :: [(String,String)] } deriving (Read, Show) +-- Returns filename for chapter number. +showChapter :: Int -> String +showChapter = printf "ch%03d.xhtml" + +-- Add identifiers to any headers without them. +addIdentifiers :: [Block] -> [Block] +addIdentifiers bs = evalState (mapM go bs) [] + where go (Header n (ident,classes,kvs) ils) = do + ids <- get + let ident' = if null ident + then uniqueIdent ils ids + else ident + put $ ident' : ids + return $ Header n (ident',classes,kvs) ils + go x = return x + -- Go through a block list and construct a table -- correlating the automatically constructed references -- that would be used in a normal pandoc document with -- new URLs to be used in the EPUB. For example, what --- was "header-1" might turn into "ch6.xhtml#header". -correlateRefs :: [Block] -> [(String,String)] -correlateRefs bs = identTable $ execState (mapM_ go bs) - IdentState{ chapterNumber = 0 - , runningIdents = [] - , chapterIdents = [] - , identTable = [] } +-- was "header-1" might turn into "ch006.xhtml#header". +correlateRefs :: Int -> [Block] -> [(String,String)] +correlateRefs chapterHeaderLevel bs = + identTable $ execState (mapM_ go bs) + IdentState{ chapterNumber = 0 + , identTable = [] } where go :: Block -> State IdentState () - go (Header n ils) = do - when (n == 1) $ - modify $ \s -> s{ chapterNumber = chapterNumber s + 1 - , chapterIdents = [] } + go (Header n (ident,_,_) _) = do + when (n <= chapterHeaderLevel) $ + modify $ \s -> s{ chapterNumber = chapterNumber s + 1 } st <- get - let runningid = uniqueIdent ils (runningIdents st) - let chapid = if n == 1 - then Nothing - else Just $ uniqueIdent ils (chapterIdents st) - modify $ \s -> s{ runningIdents = runningid : runningIdents st - , chapterIdents = maybe (chapterIdents st) - (: chapterIdents st) chapid - , identTable = (runningid, "ch" ++ show (chapterNumber st) ++ - ".xhtml" ++ maybe "" ('#':) chapid) : identTable st - } + let chapterid = showChapter (chapterNumber st) ++ + if n <= chapterHeaderLevel + then "" + else '#' : ident + modify $ \s -> s{ identTable = (ident, chapterid) : identTable st } go _ = return () -- Replace internal link references using the table produced diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs new file mode 100644 index 000000000..27f0c8305 --- /dev/null +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -0,0 +1,618 @@ +{- +Copyright (c) 2011-2012, Sergey Astanin +All rights reserved. + +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 +-} + +{- | Conversion of 'Pandoc' documents to FB2 (FictionBook2) format. + +FictionBook is an XML-based e-book format. For more information see: +<http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1> + +-} +module Text.Pandoc.Writers.FB2 (writeFB2) where + +import Control.Monad.State (StateT, evalStateT, get, modify) +import Control.Monad.State (liftM, liftM2, liftIO) +import Data.ByteString.Base64 (encode) +import Data.Char (toUpper, toLower, isSpace, isAscii, isControl) +import Data.List (intersperse, intercalate, isPrefixOf) +import Data.Either (lefts, rights) +import Network.Browser (browse, request, setAllowRedirects, setOutHandler) +import Network.HTTP (catchIO_, getRequest, getHeaders, getResponseBody) +import Network.HTTP (lookupHeader, HeaderName(..), urlEncode) +import Network.URI (isURI, unEscapeString) +import System.FilePath (takeExtension) +import Text.XML.Light +import qualified Control.Exception as E +import qualified Data.ByteString as B +import qualified Text.XML.Light as X +import qualified Text.XML.Light.Cursor as XC + +import Text.Pandoc.Definition +import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) +import Text.Pandoc.Shared (orderedListMarkers) +import Text.Pandoc.Generic (bottomUp) + +-- | Data to be written at the end of the document: +-- (foot)notes, URLs, references, images. +data FbRenderState = FbRenderState + { footnotes :: [ (Int, String, [Content]) ] -- ^ #, ID, text + , imagesToFetch :: [ (String, String) ] -- ^ filename, URL or path + , parentListMarker :: String -- ^ list marker of the parent ordered list + , parentBulletLevel :: Int -- ^ nesting level of the unordered list + , writerOptions :: WriterOptions + } deriving (Show) + +-- | FictionBook building monad. +type FBM = StateT FbRenderState IO + +newFB :: FbRenderState +newFB = FbRenderState { footnotes = [], imagesToFetch = [] + , parentListMarker = "", parentBulletLevel = 0 + , 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 :: WriterOptions -- ^ conversion options + -> Pandoc -- ^ document to convert + -> IO String -- ^ FictionBook2 document (not encoded yet) +writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do + modify (\s -> s { writerOptions = opts { writerStandalone = True } }) + desc <- description meta + fp <- frontpage meta + secs <- renderSections 1 blocks + let body = el "body" $ fp ++ secs + notes <- renderFootnotes + (imgs,missing) <- liftM imagesToFetch get >>= \s -> liftIO (fetchImages s) + let body' = replaceImagesWithAlt missing body + let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) + return $ xml_head ++ (showContent fb2_xml) + where + xml_head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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 ] + -- + frontpage :: Meta -> FBM [Content] + frontpage meta' = do + t <- cMapM toXml . docTitle $ meta' + return $ + [ el "title" (el "p" t) + , el "annotation" (map (el "p" . cMap plain) + (docAuthors meta' ++ [docDate meta'])) + ] + description :: Meta -> FBM Content + description meta' = do + bt <- booktitle meta' + let as = authors meta' + dd <- docdate meta' + return $ el "description" + [ el "title-info" (bt ++ as ++ dd) + , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version + ] + booktitle :: Meta -> FBM [Content] + booktitle meta' = do + t <- cMapM toXml . docTitle $ meta' + return $ if null t + then [] + else [ el "book-title" t ] + authors :: Meta -> [Content] + authors meta' = cMap author (docAuthors meta') + author :: [Inline] -> [Content] + author ss = + let ws = words . cMap plain $ ss + email = (el "email") `fmap` (take 1 $ filter ('@' `elem`) ws) + ws' = filter ('@' `notElem`) 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" (concat . init $ rest) + , el "last-name" (last rest) ] + ([]) -> [] + in list $ el "author" (names ++ email) + docdate :: Meta -> FBM [Content] + docdate meta' = do + let ss = docDate meta' + d <- cMapM toXml ss + return $ if null d + then [] + else [el "date" d] + +-- | Divide the stream of blocks into sections and convert to XML +-- representation. +renderSections :: Int -> [Block] -> FBM [Content] +renderSections level blocks = do + let secs = splitSections level blocks + mapM (renderSection level) secs + +renderSection :: Int -> ([Inline], [Block]) -> FBM Content +renderSection level (ttl, body) = do + title <- if null ttl + then return [] + else return . list . el "title" . formatTitle $ ttl + content <- if (hasSubsections body) + then renderSections (level + 1) body + else cMapM blockToXml body + return $ el "section" (title ++ content) + where + hasSubsections = any isHeader + isHeader (Header _ _ _) = True + isHeader _ = False + +-- | Only <p> and <empty-line> are allowed within <title> in FB2. +formatTitle :: [Inline] -> [Content] +formatTitle inlines = + let lns = split isLineBreak inlines + lns' = map (el "p" . cMap plain) lns + in intersperse (el "empty-line" ()) lns' + +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 + +-- | Divide the stream of block elements into sections: [(title, blocks)]. +splitSections :: Int -> [Block] -> [([Inline], [Block])] +splitSections level blocks = reverse $ revSplit (reverse blocks) + where + revSplit [] = [] + revSplit rblocks = + let (lastsec, before) = break sameLevel rblocks + (header, prevblocks) = + case before of + ((Header n _ title):prevblocks') -> + if n == level + then (title, prevblocks') + else ([], before) + _ -> ([], before) + in (header, reverse lastsec) : revSplit prevblocks + sameLevel (Header n _ _) = n == level + sameLevel _ = False + +-- | Make another FictionBook body with footnotes. +renderFootnotes :: FBM [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" (show 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 :: [(String,String)] -> IO ([Content],[String]) +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 :: String -> String -> IO (Either String Content) +fetchImage href link = do + mbimg <- + case (isURI link, readDataURI link) of + (True, Just (mime,_,True,base64)) -> + let mime' = map 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 + (True, Nothing) -> fetchURL link + (False, _) -> do + d <- nothingOnError $ B.readFile (unEscapeString link) + let t = case map toLower (takeExtension link) of + ".png" -> Just "image/png" + ".jpg" -> Just "image/jpeg" + ".jpeg" -> Just "image/jpeg" + ".jpe" -> Just "image/jpeg" + _ -> Nothing -- only PNG and JPEG are supported in FB2 + return $ liftM2 (,) t (liftM (toStr . encode) d) + case mbimg of + Just (imgtype, imgdata) -> do + return . Right $ el "binary" + ( [uattr "id" href + , uattr "content-type" imgtype] + , txt imgdata ) + _ -> return (Left ('#':href)) + where + nothingOnError :: (IO B.ByteString) -> (IO (Maybe B.ByteString)) + nothingOnError action = liftM Just action `E.catch` omnihandler + omnihandler :: E.SomeException -> IO (Maybe B.ByteString) + omnihandler _ = return Nothing + +-- | Extract mime type and encoded data from the Data URI. +readDataURI :: String -- ^ URI + -> Maybe (String,String,Bool,String) + -- ^ Maybe (mime,charset,isBase64,data) +readDataURI uri = + let prefix = "data:" + in if not (prefix `isPrefixOf` uri) + then Nothing + else + let rest = drop (length prefix) uri + meta = takeWhile (/= ',') rest -- without trailing ',' + uridata = drop (length meta + 1) rest + parts = 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) + | "charset=" `isPrefixOf` str = (mime,drop (length "charset=") str,enc) + | str == "base64" = (mime,cs,True) + | otherwise = m + +-- Without parameters like ;charset=...; see RFC 2045, 5.1 +isMimeType :: String -> Bool +isMimeType s = + case split (=='/') s of + [mtype,msubtype] -> + ((map toLower mtype) `elem` types + || "x-" `isPrefixOf` (map toLower mtype)) + && all valid mtype + && 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` "()<>@,;:\\\"/[]?=" + +-- | Fetch URL, return its Content-Type and binary data on success. +fetchURL :: String -> IO (Maybe (String, String)) +fetchURL url = do + flip catchIO_ (return Nothing) $ do + r <- browse $ do + setOutHandler (const (return ())) + setAllowRedirects True + liftM snd . request . getRequest $ url + let content_type = lookupHeader HdrContentType (getHeaders r) + content <- liftM (Just . toStr . encode . toBS) . getResponseBody $ Right r + return $ liftM2 (,) content_type content + where + +toBS :: String -> B.ByteString +toBS = B.pack . map (toEnum . fromEnum) + +toStr :: B.ByteString -> String +toStr = map (toEnum . fromEnum) . B.unpack + +footnoteID :: Int -> String +footnoteID i = "n" ++ (show i) + +linkID :: Int -> String +linkID i = "l" ++ (show i) + +-- | Convert a block-level Pandoc's element to FictionBook XML representation. +blockToXml :: Block -> FBM [Content] +blockToXml (Plain ss) = cMapM toXml ss -- FIXME: can lead to malformed FB2 +blockToXml (Para [Math DisplayMath formula]) = insertMath NormalImage formula +-- title beginning with fig: indicates that the image is a figure +blockToXml (Para [Image alt (src,'f':'i':'g':':':tit)]) = + insertImage NormalImage (Image alt (src,tit)) +blockToXml (Para ss) = liftM (list . el "p") $ cMapM toXml ss +blockToXml (CodeBlock _ s) = return . spaceBeforeAfter . + map (el "p" . el "code") . lines $ s +blockToXml (RawBlock _ s) = return . spaceBeforeAfter . + map (el "p" . el "code") . lines $ s +blockToXml (BlockQuote bs) = liftM (list . el "cite") $ cMapM blockToXml bs +blockToXml (OrderedList a bss) = do + state <- get + let pmrk = parentListMarker state + let markers = map ((pmrk ++ " ") ++) $ orderedListMarkers a + let mkitem mrk bs = do + modify (\s -> s { parentListMarker = mrk }) + itemtext <- cMapM blockToXml . paraToPlain $ bs + modify (\s -> s { parentListMarker = pmrk }) -- old parent marker + return . el "p" $ [ txt mrk, txt " " ] ++ itemtext + mapM (uncurry mkitem) (zip markers bss) +blockToXml (BulletList bss) = do + state <- get + let level = parentBulletLevel state + let pmrk = parentListMarker state + let prefix = replicate (length pmrk) ' ' + let bullets = ["\x2022", "\x25e6", "*", "\x2043", "\x2023"] + let mrk = prefix ++ bullets !! (level `mod` (length bullets)) + let mkitem bs = do + modify (\s -> s { parentBulletLevel = (level+1) }) + itemtext <- cMapM blockToXml . paraToPlain $ bs + modify (\s -> s { parentBulletLevel = level }) -- restore bullet level + return $ el "p" $ [ txt (mrk ++ " ") ] ++ itemtext + mapM mkitem bss +blockToXml (DefinitionList defs) = + cMapM mkdef defs + where + mkdef (term, bss) = do + def' <- cMapM (cMapM blockToXml . sep . paraToPlain . map indent) bss + t <- wrap "strong" term + return [ el "p" t, el "p" def' ] + sep blocks = + if all needsBreak blocks then + blocks ++ [Plain [LineBreak]] + else + blocks + needsBreak (Para _) = False + needsBreak (Plain ins) = LineBreak `notElem` ins + needsBreak _ = True +blockToXml (Header _ _ _) = -- should never happen, see renderSections + error "unexpected header in section text" +blockToXml HorizontalRule = return + [ el "empty-line" () + , el "p" (txt (replicate 10 '—')) + , el "empty-line" () ] +blockToXml (Table caption aligns _ headers rows) = do + hd <- mkrow "th" headers aligns + bd <- mapM (\r -> mkrow "td" r aligns) rows + c <- return . el "emphasis" =<< cMapM toXml caption + return [el "table" (hd : bd), el "p" c] + where + mkrow :: String -> [TableCell] -> [Alignment] -> FBM Content + mkrow tag cells aligns' = + (el "tr") `liftM` (mapM (mkcell tag) (zip cells aligns')) + -- + mkcell :: String -> (TableCell, Alignment) -> FBM 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 Null = return [] + +-- Replace paragraphs with plain text and line break. +-- Necessary to simulate multi-paragraph lists in FB2. +paraToPlain :: [Block] -> [Block] +paraToPlain [] = [] +paraToPlain (Para inlines : rest) = + let p = (Plain (inlines ++ [LineBreak])) + in p : paraToPlain rest +paraToPlain (p:rest) = p : paraToPlain rest + +-- Simulate increased indentation level. Will not really work +-- for multi-line paragraphs. +indent :: Block -> Block +indent = indentBlock + where + -- indentation space + spacer :: String + spacer = replicate 4 ' ' + -- + indentBlock (Plain ins) = Plain ((Str spacer):ins) + indentBlock (Para ins) = Para ((Str spacer):ins) + indentBlock (CodeBlock a s) = + let s' = unlines . map (spacer++) . 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 + +-- | Convert a Pandoc's Inline element to FictionBook XML representation. +toXml :: Inline -> FBM [Content] +toXml (Str s) = return [txt s] +toXml (Emph ss) = list `liftM` wrap "emphasis" 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 $ bottomUp (map toUpper) 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 LineBreak = return [el "empty-line" ()] +toXml (Math _ formula) = insertMath InlineImage formula +toXml (RawInline _ _) = return [] -- raw TeX and raw HTML are suppressed +toXml (Link text (url,ttl)) = do + fns <- footnotes `liftM` get + let n = 1 + length fns + let ln_id = linkID n + let ln_ref = list . el "sup" . txt $ "[" ++ show n ++ "]" + ln_text <- cMapM toXml text + let ln_desc = + let ttl' = dropWhile isSpace ttl + in if null ttl' + then list . el "p" $ el "code" url + else list . el "p" $ [ txt (ttl' ++ ": "), el "code" url ] + modify (\s -> s { footnotes = (n, ln_id, ln_desc) : fns }) + return $ ln_text ++ + [ el "a" + ( [ attr ("l","href") ('#':ln_id) + , uattr "type" "note" ] + , ln_ref) ] +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 = el "sup" . txt $ "[" ++ show n ++ "]" + return . list $ el "a" ( [ attr ("l","href") ('#':fn_id) + , uattr "type" "note" ] + , fn_ref ) + +insertMath :: ImageMode -> String -> FBM [Content] +insertMath immode formula = do + htmlMath <- return . writerHTMLMathMethod . writerOptions =<< get + case htmlMath of + WebTeX url -> do + let alt = [Code nullAttr formula] + let imgurl = url ++ urlEncode formula + let img = Image alt (imgurl, "") + insertImage immode img + _ -> return [el "code" formula] + +insertImage :: ImageMode -> Inline -> FBM [Content] +insertImage immode (Image alt (url,ttl)) = do + images <- imagesToFetch `liftM` get + let n = 1 + length images + let fname = "image" ++ show n + modify (\s -> s { imagesToFetch = (fname, url) : images }) + let ttlattr = case (immode, null ttl) of + (NormalImage, False) -> [ uattr "title" ttl ] + _ -> [] + return . list $ + el "image" $ + [ attr ("l","href") ('#':fname) + , attr ("l","type") (show immode) + , uattr "alt" (cMap plain alt) ] + ++ ttlattr +insertImage _ _ = error "unexpected inline instead of image" + +replaceImagesWithAlt :: [String] -> Content -> Content +replaceImagesWithAlt missingHrefs body = + let cur = XC.fromContent body + cur' = replaceAll cur + in XC.toTree . XC.root $ cur' + where + -- + replaceAll :: XC.Cursor -> XC.Cursor + replaceAll c = + let n = XC.current c + c' = if isImage n && isMissing n + then XC.modifyContent replaceNode c + else c + in case XC.nextDF c' of + (Just cnext) -> replaceAll cnext + Nothing -> c' -- end of document + -- + isImage :: Content -> Bool + isImage (Elem e) = (elName e) == (uname "image") + isImage _ = False + -- + 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 (uname "alt") + imtype = getAttrVal attrs (qname "l" "type") + in case (alt, imtype) of + (Just alt', Just imtype') -> + if imtype' == show 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 String + 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 :: String -> [Inline] -> FBM 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 -> String +plain (Str s) = s +plain (Emph ss) = concat (map plain ss) +plain (Strong ss) = concat (map plain ss) +plain (Strikeout ss) = concat (map plain ss) +plain (Superscript ss) = concat (map plain ss) +plain (Subscript ss) = concat (map plain ss) +plain (SmallCaps ss) = concat (map plain ss) +plain (Quoted _ ss) = concat (map plain ss) +plain (Cite _ ss) = concat (map plain ss) -- FIXME +plain (Code _ s) = s +plain Space = " " +plain LineBreak = "\n" +plain (Math _ s) = s +plain (RawInline _ s) = s +plain (Link text (url,_)) = concat (map plain text ++ [" <", url, ">"]) +plain (Image alt _) = concat (map plain alt) +plain (Note _) = "" -- FIXME + +-- | Create an XML element. +el :: (Node t) + => String -- ^ 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 :: String -> Content +txt s = Text $ CData CDataText s Nothing + +-- | Create an XML attribute with an unqualified name. +uattr :: String -> String -> Text.XML.Light.Attr +uattr name val = Attr (uname name) val + +-- | Create an XML attribute with a qualified name from given namespace. +attr :: (String, String) -> String -> Text.XML.Light.Attr +attr (ns, name) val = Attr (qname ns name) val + +-- | Unqualified name +uname :: String -> QName +uname name = QName name Nothing Nothing + +-- | Qualified name +qname :: String -> String -> 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 diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index b8474ee3f..32d52f3e7 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -32,13 +32,14 @@ Conversion of 'Pandoc' documents to HTML. module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where import Text.Pandoc.Definition import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Templates import Text.Pandoc.Generic import Text.Pandoc.Readers.TeXMath import Text.Pandoc.Slides import Text.Pandoc.Highlighting ( highlight, styleToCss, formatHtmlInline, formatHtmlBlock ) -import Text.Pandoc.XML (stripTags, escapeStringForXML, fromEntities) +import Text.Pandoc.XML (stripTags, fromEntities) import Network.HTTP ( urlEncode ) import Numeric ( showHex ) import Data.Char ( ord, toLower ) @@ -52,14 +53,18 @@ import Text.Blaze.Internal(preEscapedString) #else import Text.Blaze #endif +#if MIN_VERSION_blaze_html(0,5,1) +import qualified Text.Blaze.XHtml5 as H5 +#else import qualified Text.Blaze.Html5 as H5 +#endif import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A import Text.Blaze.Renderer.String (renderHtml) import Text.TeXMath import Text.XML.Light.Output import System.FilePath (takeExtension) -import Data.Monoid (mempty, mconcat) +import Data.Monoid data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes @@ -76,8 +81,11 @@ defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, -- Helpers to render HTML with the appropriate function. strToHtml :: String -> Html -strToHtml = preEscapedString . escapeStringForXML --- strToHtml = toHtml +strToHtml ('\'':xs) = preEscapedString "\'" `mappend` strToHtml xs +strToHtml xs@(_:_) = case break (=='\'') xs of + (_ ,[]) -> toHtml xs + (ys,zs) -> toHtml ys `mappend` strToHtml zs +strToHtml [] = "" -- | Hard linebreak. nl :: WriterOptions -> Html @@ -211,7 +219,10 @@ inTemplate opts tit auths authsMeta date toc body' newvars = -- | Like Text.XHtml's identifier, but adds the writerIdentifierPrefix prefixedId :: WriterOptions -> String -> Attribute -prefixedId opts s = A.id $ toValue $ writerIdentifierPrefix opts ++ s +prefixedId opts s = + case s of + "" -> mempty + _ -> A.id $ toValue $ writerIdentifierPrefix opts ++ s -- | Replacement for Text.XHtml's unordList. unordList :: WriterOptions -> ([Html] -> Html) @@ -239,8 +250,8 @@ showSecNum = concat . intersperse "." . map show -- | Converts an Element to a list item for a table of contents, -- retrieving the appropriate identifier from state. elementToListItem :: WriterOptions -> Element -> State WriterState (Maybe Html) -elementToListItem _ (Blk _) = return Nothing -elementToListItem opts (Sec _ num id' headerText subsecs) = do +elementToListItem opts (Sec lev num id' headerText subsecs) + | lev <= writerTOCDepth opts = do let sectnum = if writerNumberSections opts then (H.span ! A.class_ "toc-section-number" $ toHtml $ showSecNum num) >> preEscapedString " " @@ -250,8 +261,12 @@ elementToListItem opts (Sec _ num id' headerText subsecs) = do let subList = if null subHeads then mempty else unordList opts subHeads - return $ Just $ (H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ id') + return $ Just + $ if null id' + then (H.a $ toHtml txt) >> subList + else (H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ id') $ toHtml txt) >> subList +elementToListItem _ _ = return Nothing -- | Convert an Element to Html. elementToHtml :: Int -> WriterOptions -> Element -> State WriterState Html @@ -264,7 +279,7 @@ elementToHtml slideLevel opts (Sec level num id' title' elements) = do let titleSlide = slide && level < slideLevel header' <- if title' == [Str "\0"] -- marker for hrule then return mempty - else blockToHtml opts (Header level' title') + else blockToHtml opts (Header level' (id',[],[]) title') let isSec (Sec _ _ _ _ _) = True isSec (Blk _) = False innerContents <- mapM (elementToHtml slideLevel opts) @@ -272,8 +287,9 @@ elementToHtml slideLevel opts (Sec level num id' title' elements) = do -- title slides have no content of their own then filter isSec elements else elements - let header'' = if (writerStrictMarkdown opts || writerSectionDivs opts || - writerSlideVariant opts == S5Slides || slide) + let header'' = if (writerSectionDivs opts || + writerSlideVariant opts == S5Slides || + slide) then header' else header' ! prefixedId opts id' let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] @@ -376,15 +392,20 @@ treatAsImage fp = blockToHtml :: WriterOptions -> Block -> State WriterState Html blockToHtml _ Null = return mempty blockToHtml opts (Plain lst) = inlineListToHtml opts lst -blockToHtml opts (Para [Image txt (s,tit)]) = do +-- title beginning with fig: indicates that the image is a figure +blockToHtml opts (Para [Image txt (s,'f':'i':'g':':':tit)]) = do img <- inlineToHtml opts (Image txt (s,tit)) - capt <- inlineListToHtml opts txt + let tocapt = if writerHtml5 opts + then H5.figcaption + else H.p ! A.class_ "caption" + capt <- if null txt + then return mempty + else tocapt `fmap` inlineListToHtml opts txt return $ if writerHtml5 opts then H5.figure $ mconcat - [nl opts, img, H5.figcaption capt, nl opts] + [nl opts, img, capt, nl opts] else H.div ! A.class_ "figure" $ mconcat - [nl opts, img, H.p ! A.class_ "caption" $ capt, - nl opts] + [nl opts, img, capt, nl opts] blockToHtml opts (Para lst) = do contents <- inlineListToHtml opts lst return $ H.p contents @@ -392,7 +413,7 @@ blockToHtml _ (RawBlock "html" str) = return $ preEscapedString str blockToHtml _ (RawBlock _ _) = return mempty blockToHtml opts (HorizontalRule) = return $ if writerHtml5 opts then H5.hr else H.hr blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do - let tolhs = writerLiterateHaskell opts && + let tolhs = isEnabled Ext_literate_haskell opts && any (\c -> map toLower c == "haskell") classes && any (\c -> map toLower c == "literate") classes classes' = if tolhs @@ -427,15 +448,16 @@ blockToHtml opts (BlockQuote blocks) = else do contents <- blockListToHtml opts blocks return $ H.blockquote $ nl opts >> contents >> nl opts -blockToHtml opts (Header level lst) = do +blockToHtml opts (Header level (ident,_,_) lst) = do contents <- inlineListToHtml opts lst secnum <- liftM stSecNum get let contents' = if writerNumberSections opts then (H.span ! A.class_ "header-section-number" $ toHtml $ showSecNum secnum) >> strToHtml " " >> contents else contents - let contents'' = if writerTableOfContents opts - then H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ "TOC") $ contents' + let contents'' = if writerTableOfContents opts && not (null ident) + then H.a ! A.href (toValue $ + '#' : writerIdentifierPrefix opts ++ ident) $ contents' else contents' return $ (case level of 1 -> H.h1 contents'' @@ -477,10 +499,13 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do return $ foldl (!) (ordList opts contents) attribs blockToHtml opts (DefinitionList lst) = do contents <- mapM (\(term, defs) -> - do term' <- liftM (H.dt) $ inlineListToHtml opts term + do term' <- if null term + then return mempty + else liftM (H.dt) $ inlineListToHtml opts term defs' <- mapM ((liftM (\x -> H.dd $ (x >> nl opts))) . blockListToHtml opts) defs - return $ mconcat $ nl opts : term' : nl opts : defs') lst + return $ mconcat $ nl opts : term' : nl opts : + intersperse (nl opts) defs') lst let lst' = H.dl $ mconcat contents >> nl opts let lst'' = if writerIncremental opts then lst' ! A.class_ "incremental" @@ -576,7 +601,9 @@ inlineToHtml opts inline = Nothing -> return $ foldl (!) H.code (attrsToHtml opts attr) $ strToHtml str - Just h -> return $ foldl (!) h $ + Just h -> do + modify $ \st -> st{ stHighlighting = True } + return $ foldl (!) h $ attrsToHtml opts (id',[],keyvals) where (id',_,keyvals) = attr (Strikeout lst) -> inlineListToHtml opts lst >>= @@ -591,7 +618,7 @@ inlineToHtml opts inline = strToHtml "’") DoubleQuote -> (strToHtml "“", strToHtml "”") - in if writerHtml5 opts + in if writerHtmlQTags opts then do modify $ \st -> st{ stQuotes = True } H.q `fmap` inlineListToHtml opts lst @@ -618,7 +645,7 @@ inlineToHtml opts inline = ! A.src (toValue $ url ++ urlEncode str) ! A.alt (toValue str) ! A.title (toValue str) - let brtag = if writerHtml5 opts then H5.br else H.br + let brtag = if writerHtml5 opts then H5.br else H.br return $ case t of InlineMath -> m DisplayMath -> brtag >> m >> brtag @@ -638,7 +665,7 @@ inlineToHtml opts inline = Left _ -> inlineListToHtml opts (readTeXMath str) >>= return . (H.span ! A.class_ "math") - MathJax _ -> return $ toHtml $ + MathJax _ -> return $ H.span ! A.class_ "math" $ toHtml $ case t of InlineMath -> "\\(" ++ str ++ "\\)" DisplayMath -> "\\[" ++ str ++ "\\]" @@ -655,7 +682,9 @@ inlineToHtml opts inline = _ -> return mempty (RawInline "html" str) -> return $ preEscapedString str (RawInline _ _) -> return mempty - (Link [Code _ str] (s,_)) | "mailto:" `isPrefixOf` s -> + (Link [Str str] (s,_)) | "mailto:" `isPrefixOf` s && + s == escapeURI ("mailto" ++ str) -> + -- autolink return $ obfuscateLink opts str s (Link txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt @@ -693,13 +722,21 @@ inlineToHtml opts inline = htmlContents <- blockListToNote opts ref contents -- push contents onto front of notes put $ st {stNotes = (htmlContents:notes)} - return $ H.sup $ - H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ "fn" ++ ref) - ! A.class_ "footnoteRef" - ! prefixedId opts ("fnref" ++ ref) - $ toHtml ref - (Cite _ il) -> do contents <- inlineListToHtml opts il - return $ H.span ! A.class_ "citation" $ contents + let link = H.a ! A.href (toValue $ "#" ++ + writerIdentifierPrefix opts ++ "fn" ++ ref) + ! A.class_ "footnoteRef" + ! prefixedId opts ("fnref" ++ ref) + $ toHtml ref + let link' = case writerEpubVersion opts of + Just EPUB3 -> link ! customAttribute "epub:type" "noteref" + _ -> link + return $ H.sup $ link' + (Cite cits il)-> do contents <- inlineListToHtml opts il + let citationIds = unwords $ map citationId cits + let result = H.span ! A.class_ "citation" $ contents + return $ if writerHtml5 opts + then result ! customAttribute "data-cites" (toValue citationIds) + else result blockListToNote :: WriterOptions -> String -> [Block] -> State WriterState Html blockListToNote opts ref blocks = @@ -718,4 +755,8 @@ blockListToNote opts ref blocks = _ -> otherBlocks ++ [lastBlock, Plain backlink] in do contents <- blockListToHtml opts blocks' - return $ nl opts >> (H.li ! (prefixedId opts ("fn" ++ ref)) $ contents) + let noteItem = H.li ! (prefixedId opts ("fn" ++ ref)) $ contents + let noteItem' = case writerEpubVersion opts of + Just EPUB3 -> noteItem ! customAttribute "epub:type" "footnote" + _ -> noteItem + return $ nl opts >> noteItem' diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 7beee2d42..3d3e6ae5d 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -32,13 +32,16 @@ module Text.Pandoc.Writers.LaTeX ( writeLaTeX ) where import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Templates import Text.Printf ( printf ) +import qualified Data.Map as M import Network.URI ( isAbsoluteURI, unEscapeString ) import Data.List ( (\\), isSuffixOf, isInfixOf, isPrefixOf, intercalate, intersperse ) import Data.Char ( toLower, isPunctuation ) import Control.Monad.State +import Control.Applicative ((<|>)) import Text.Pandoc.Pretty import System.FilePath (dropExtension) import Text.Pandoc.Slides @@ -48,12 +51,10 @@ import Text.Pandoc.Highlighting (highlight, styleToLaTeX, data WriterState = WriterState { stInNote :: Bool -- true if we're in a note , stInTable :: Bool -- true if we're in a table - , stTableNotes :: [(Char, Doc)] -- List of markers, notes - -- in current table + , stTableNotes :: [Doc] -- List of notes in current table , 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 - , stEnumerate :: Bool -- true if document needs fancy enumerated lists , stTable :: Bool -- true if document has a table , stStrikeout :: Bool -- true if document has strikeout , stUrl :: Bool -- true if document has visible URL link @@ -73,7 +74,7 @@ writeLaTeX options document = evalState (pandocToLaTeX options document) $ WriterState { stInNote = False, stInTable = False, stTableNotes = [], stOLLevel = 1, stOptions = options, - stVerbInNote = False, stEnumerate = False, + stVerbInNote = False, stTable = False, stStrikeout = False, stUrl = False, stGraphics = False, stLHS = False, stBook = writerChapters options, @@ -110,8 +111,8 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do let (blocks', lastHeader) = if writerCiteMethod options == Citeproc then (blocks, []) else case last blocks of - Header 1 il -> (init blocks, il) - _ -> (blocks, []) + Header 1 _ il -> (init blocks, il) + _ -> (blocks, []) blocks'' <- if writerBeamer options then toSlides blocks' else return blocks' @@ -132,6 +133,10 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do _ -> [] context = writerVariables options ++ [ ("toc", if writerTableOfContents options then "yes" else "") + , ("toc-depth", show (writerTOCDepth options - + if writerChapters options + then 1 + else 0)) , ("body", main) , ("title", titletext) , ("title-meta", stringify title) @@ -144,7 +149,6 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do else "article") ] ++ [ ("author", a) | a <- authorsText ] ++ [ ("verbatim-in-note", "yes") | stVerbInNote st ] ++ - [ ("fancy-enums", "yes") | stEnumerate st ] ++ [ ("tables", "yes") | stTable st ] ++ [ ("strikeout", "yes") | stStrikeout st ] ++ [ ("url", "yes") | stUrl st ] ++ @@ -189,7 +193,7 @@ stringToLaTeX isUrl (x:xs) = do '$' -> "\\$" ++ rest '%' -> "\\%" ++ rest '&' -> "\\&" ++ rest - '_' -> "\\_" ++ rest + '_' | not isUrl -> "\\_" ++ rest '#' -> "\\#" ++ rest '-' -> case xs of -- prevent adjacent hyphens from forming ligatures ('-':_) -> "-{}" ++ rest @@ -212,6 +216,13 @@ stringToLaTeX isUrl (x:xs) = do '\x2013' | ligatures -> "--" ++ rest _ -> x : rest +-- This is needed because | in math mode interacts badly with +-- highlighting-kate, which redefines | as a short verb command. +escapeMath :: String -> String +escapeMath ('|':xs) = "\\vert " ++ escapeMath xs +escapeMath (x:xs) = x : escapeMath xs +escapeMath [] = "" + -- | Puts contents into LaTeX command. inCmd :: String -> Doc -> Doc inCmd cmd contents = char '\\' <> text cmd <> braces contents @@ -225,7 +236,7 @@ toSlides bs = do elementToBeamer :: Int -> Element -> State WriterState [Block] elementToBeamer _slideLevel (Blk b) = return [b] -elementToBeamer slideLevel (Sec lvl _num _ident tit elts) +elementToBeamer slideLevel (Sec lvl _num ident tit elts) | lvl > slideLevel = do bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts return $ Para ( RawInline "latex" "\\begin{block}{" @@ -233,14 +244,18 @@ elementToBeamer slideLevel (Sec lvl _num _ident tit elts) : bs ++ [RawBlock "latex" "\\end{block}"] | lvl < slideLevel = do bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts - return $ (Header lvl tit) : bs + return $ (Header lvl (ident,[],[]) tit) : bs | otherwise = do -- lvl == slideLevel -- note: [fragile] is required or verbatim breaks let hasCodeBlock (CodeBlock _ _) = [True] hasCodeBlock _ = [] let hasCode (Code _ _) = [True] hasCode _ = [] - let fragile = if not $ null $ queryWith hasCodeBlock elts ++ queryWith hasCode elts + opts <- gets stOptions + let fragile = if not $ null $ queryWith hasCodeBlock elts ++ + if writerListings opts + then queryWith hasCode elts + else [] then "[fragile]" else "" let slideStart = Para $ RawInline "latex" ("\\begin{frame}" ++ fragile) : @@ -259,19 +274,27 @@ isListBlock (OrderedList _ _) = True isListBlock (DefinitionList _) = True isListBlock _ = False +isLineBreakOrSpace :: Inline -> Bool +isLineBreakOrSpace LineBreak = True +isLineBreakOrSpace Space = True +isLineBreakOrSpace _ = False + -- | Convert Pandoc block element to LaTeX. blockToLaTeX :: Block -- ^ Block to convert -> State WriterState Doc blockToLaTeX Null = return empty -blockToLaTeX (Plain lst) = inlineListToLaTeX lst -blockToLaTeX (Para [Image txt (src,tit)]) = do - capt <- inlineListToLaTeX txt +blockToLaTeX (Plain lst) = + inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst +-- title beginning with fig: indicates that the image is a figure +blockToLaTeX (Para [Image txt (src,'f':'i':'g':':':tit)]) = do + capt <- if null txt + then return empty + else (\c -> "\\caption" <> braces c) `fmap` inlineListToLaTeX txt img <- inlineToLaTeX (Image txt (src,tit)) return $ "\\begin{figure}[htbp]" $$ "\\centering" $$ img $$ - ("\\caption{" <> capt <> char '}') $$ "\\end{figure}" -blockToLaTeX (Para lst) = do - result <- inlineListToLaTeX lst - return result + capt $$ "\\end{figure}" +blockToLaTeX (Para lst) = + inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst blockToLaTeX (BlockQuote lst) = do beamer <- writerBeamer `fmap` gets stOptions case lst of @@ -287,7 +310,7 @@ blockToLaTeX (BlockQuote lst) = do blockToLaTeX (CodeBlock (_,classes,keyvalAttr) str) = do opts <- gets stOptions case () of - _ | writerLiterateHaskell opts && "haskell" `elem` classes && + _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes && "literate" `elem` classes -> lhsCodeBlock | writerListings opts -> listingsCodeBlock | writerHighlight opts && not (null classes) -> highlightedCodeBlock @@ -306,25 +329,9 @@ blockToLaTeX (CodeBlock (_,classes,keyvalAttr) str) = do listingsCodeBlock = do st <- get let params = if writerListings (stOptions st) - then take 1 - [ "language=" ++ lang | lang <- classes - , lang `elem` ["ABAP","IDL","Plasm","ACSL","inform" - ,"POV","Ada","Java","Prolog","Algol" - ,"JVMIS","Promela","Ant","ksh","Python" - ,"Assembler","Lisp","R","Awk","Logo" - ,"Reduce","bash","make","Rexx","Basic" - ,"Mathematica","RSL","C","Matlab","Ruby" - ,"C++","Mercury","S","Caml","MetaPost" - ,"SAS","Clean","Miranda","Scilab","Cobol" - ,"Mizar","sh","Comal","ML","SHELXL","csh" - ,"Modula-2","Simula","Delphi","MuPAD" - ,"SQL","Eiffel","NASTRAN","tcl","Elan" - ,"Oberon-2","TeX","erlang","OCL" - ,"VBScript","Euphoria","Octave","Verilog" - ,"Fortran","Oz","VHDL","GCL","Pascal" - ,"VRML","Gnuplot","Perl","XML","Haskell" - ,"PHP","XSLT","HTML","PL/I"] - ] ++ + then (case getListingsLanguage classes of + Just l -> [ "language=" ++ l ] + Nothing -> []) ++ [ key ++ "=" ++ attr | (key,attr) <- keyvalAttr ] else [] printParams @@ -343,7 +350,10 @@ blockToLaTeX (BulletList lst) = do incremental <- gets stIncremental let inc = if incremental then "[<+->]" else "" items <- mapM listItemToLaTeX lst - return $ text ("\\begin{itemize}" ++ inc) $$ vcat items $$ + let spacing = if isTightList lst + then text "\\itemsep1pt\\parskip0pt\\parsep0pt" + else empty + return $ text ("\\begin{itemize}" ++ inc) $$ spacing $$ vcat items $$ "\\end{itemize}" blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do st <- get @@ -352,54 +362,74 @@ blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do put $ st {stOLLevel = oldlevel + 1} items <- mapM listItemToLaTeX lst modify (\s -> s {stOLLevel = oldlevel}) - exemplar <- if numstyle /= DefaultStyle || numdelim /= DefaultDelim - then do - modify $ \s -> s{ stEnumerate = True } - return $ char '[' <> - text (head (orderedListMarkers (1, numstyle, - numdelim))) <> char ']' - else return empty - let resetcounter = if start /= 1 && oldlevel <= 4 - then text $ "\\setcounter{enum" ++ - map toLower (toRomanNumeral oldlevel) ++ - "}{" ++ show (start - 1) ++ "}" - else empty - return $ text ("\\begin{enumerate}" ++ inc) <> exemplar $$ resetcounter $$ - vcat items $$ "\\end{enumerate}" + 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 enum = text $ "enum" ++ map toLower (toRomanNumeral oldlevel) + let stylecommand = if numstyle == DefaultStyle && numdelim == DefaultDelim + then empty + else "\\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 "\\itemsep1pt\\parskip0pt\\parsep0pt" + else empty + return $ text ("\\begin{enumerate}" ++ inc) + $$ stylecommand + $$ resetcounter + $$ spacing + $$ vcat items + $$ "\\end{enumerate}" blockToLaTeX (DefinitionList lst) = do incremental <- gets stIncremental let inc = if incremental then "[<+->]" else "" items <- mapM defListItemToLaTeX lst - return $ text ("\\begin{description}" ++ inc) $$ vcat items $$ + let spacing = if and $ map isTightList (map snd lst) + then text "\\itemsep1pt\\parskip0pt\\parsep0pt" + else empty + return $ text ("\\begin{description}" ++ inc) $$ spacing $$ vcat items $$ "\\end{description}" blockToLaTeX HorizontalRule = return $ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}" -blockToLaTeX (Header level lst) = sectionHeader "" level lst +blockToLaTeX (Header level (id',_,_) lst) = sectionHeader id' level lst blockToLaTeX (Table caption aligns widths heads rows) = do modify $ \s -> s{ stInTable = True, stTableNotes = [] } headers <- if all null heads then return empty - else liftM ($$ "\\ML") - $ (tableRowToLaTeX True aligns widths) heads + else ($$ "\\hline\\noalign{\\medskip}") `fmap` + (tableRowToLaTeX True aligns widths) heads captionText <- inlineListToLaTeX caption let capt = if isEmpty captionText then empty - else text "caption = {" <> captionText <> "}," <> space + else text "\\noalign{\\medskip}" + $$ text "\\caption" <> braces captionText rows' <- mapM (tableRowToLaTeX False aligns widths) rows - let rows'' = intersperse ("\\\\\\noalign{\\medskip}") rows' tableNotes <- liftM (reverse . stTableNotes) get - let toNote (marker, x) = "\\tnote" <> brackets (char marker) <> - braces (nest 2 x) + let toNote x = "\\footnotetext" <> braces (nest 2 x) let notes = vcat $ map toNote tableNotes let colDescriptors = text $ concat $ map toColDescriptor aligns - let tableBody = - ("\\ctable" <> brackets (capt <> text "pos = H, center, botcap")) - <> braces colDescriptors - $$ braces ("% notes" <> cr <> notes <> cr) - $$ braces (text "% rows" $$ "\\FL" $$ - vcat (headers : rows'') $$ "\\LL" <> cr) modify $ \s -> s{ stTable = True, stInTable = False, stTableNotes = [] } - return $ tableBody + return $ "\\begin{longtable}[c]" <> braces colDescriptors + $$ "\\hline\\noalign{\\medskip}" + $$ headers + $$ vcat rows' + $$ "\\hline" + $$ capt + $$ notes + $$ "\\end{longtable}" toColDescriptor :: Alignment -> String toColDescriptor align = @@ -426,11 +456,11 @@ tableRowToLaTeX header aligns widths cols = do AlignCenter -> "\\centering" AlignDefault -> "\\raggedright" let toCell 0 _ c = c - toCell w a c = "\\parbox" <> valign <> + toCell w a c = "\\begin{minipage}" <> valign <> braces (text (printf "%.2f\\columnwidth" w)) <> - braces (halign a <> cr <> c <> cr) + (halign a <> cr <> c <> cr) <> "\\end{minipage}" let cells = zipWith3 toCell widths aligns renderedCells - return $ hcat $ intersperse (" & ") cells + return $ hsep (intersperse "&" cells) $$ "\\\\\\noalign{\\medskip}" listItemToLaTeX :: [Block] -> State WriterState Doc listItemToLaTeX lst = blockListToLaTeX lst >>= return . (text "\\item" $$) . @@ -487,7 +517,19 @@ sectionHeader ref level lst = do -- | Convert list of inline elements to LaTeX. inlineListToLaTeX :: [Inline] -- ^ Inlines to convert -> State WriterState Doc -inlineListToLaTeX lst = mapM inlineToLaTeX lst >>= return . hcat +inlineListToLaTeX lst = + mapM inlineToLaTeX (fixLineInitialSpaces lst) + >>= return . hcat + -- nonbreaking spaces (~) in LaTeX don't work after line breaks, + -- so we turn nbsps after hard breaks to \hspace commands. + -- this is mostly used in verse. + where fixLineInitialSpaces [] = [] + fixLineInitialSpaces (LineBreak : Str s@('\160':_) : xs) = + LineBreak : fixNbsps s ++ fixLineInitialSpaces xs + fixLineInitialSpaces (x:xs) = x : fixLineInitialSpaces xs + fixNbsps s = let (ys,zs) = span (=='\160') s + in replicate (length ys) hspace ++ [Str zs] + hspace = RawInline "latex" "\\hspace*{0.333em}" isQuoted :: Inline -> Bool isQuoted (Quoted _ _) = True @@ -560,8 +602,10 @@ inlineToLaTeX (Quoted qt lst) = do then char '`' <> inner <> char '\'' else char '\x2018' <> inner <> char '\x2019' inlineToLaTeX (Str str) = liftM text $ stringToLaTeX False str -inlineToLaTeX (Math InlineMath str) = return $ char '$' <> text str <> char '$' -inlineToLaTeX (Math DisplayMath str) = return $ "\\[" <> text str <> "\\]" +inlineToLaTeX (Math InlineMath str) = + return $ char '$' <> text (escapeMath str) <> char '$' +inlineToLaTeX (Math DisplayMath str) = + return $ "\\[" <> text (escapeMath str) <> "\\]" inlineToLaTeX (RawInline "latex" str) = return $ text str inlineToLaTeX (RawInline "tex" str) = return $ text str inlineToLaTeX (RawInline _ _) = return empty @@ -569,13 +613,14 @@ inlineToLaTeX (LineBreak) = return "\\\\" inlineToLaTeX Space = return space inlineToLaTeX (Link txt ('#':ident, _)) = do contents <- inlineListToLaTeX txt - ident' <- stringToLaTeX False ident + ident' <- stringToLaTeX True ident return $ text "\\hyperref" <> brackets (text ident') <> braces contents inlineToLaTeX (Link txt (src, _)) = case txt of - [Code _ x] | x == src -> -- autolink + [Str x] | x == src -> -- autolink do modify $ \s -> s{ stUrl = True } - return $ text $ "\\url{" ++ x ++ "}" + src' <- stringToLaTeX True x + return $ text $ "\\url{" ++ src' ++ "}" _ -> do contents <- inlineListToLaTeX txt src' <- stringToLaTeX True src return $ text ("\\href{" ++ src' ++ "}{") <> @@ -597,9 +642,8 @@ inlineToLaTeX (Note contents) = do if inTable then do curnotes <- liftM stTableNotes get - let marker = cycle ['a'..'z'] !! length curnotes - modify $ \s -> s{ stTableNotes = (marker, contents') : curnotes } - return $ "\\tmark" <> brackets (char marker) <> space + modify $ \s -> s{ stTableNotes = contents' : curnotes } + return $ "\\footnotemark" <> space else return $ "\\footnote" <> braces (nest 2 contents' <> optnl) -- note: a \n before } needed when note ends with a Verbatim environment @@ -697,3 +741,61 @@ citationsToBiblatex (c:cs) = do = citeArguments p s k citationsToBiblatex _ = return empty + +-- correlate pandoc language names with listings names +langsMap :: M.Map String String +langsMap = M.fromList + [("ada","Ada") + ,("java","Java") + ,("prolog","Prolog") + ,("python","Python") + ,("gnuassembler","Assembler") + ,("commonlisp","Lisp") + ,("r","R") + ,("awk","Awk") + ,("bash","bash") + ,("makefile","make") + ,("c","C") + ,("matlab","Matlab") + ,("ruby","Ruby") + ,("cpp","C++") + ,("ocaml","Caml") + ,("modula2","Modula-2") + ,("sql","SQL") + ,("eiffel","Eiffel") + ,("tcl","tcl") + ,("erlang","erlang") + ,("verilog","Verilog") + ,("fortran","Fortran") + ,("vhdl","VHDL") + ,("pascal","Pascal") + ,("perl","Perl") + ,("xml","XML") + ,("haskell","Haskell") + ,("php","PHP") + ,("xslt","XSLT") + ,("html","HTML") + ] + +listingsLangs :: [String] +listingsLangs = ["Ada","Java","Prolog","Algol","JVMIS","Promela", + "Ant","ksh","Python","Assembler","Lisp","R","Awk", + "Logo","Reduce","bash","make","Rexx","Basic", + "Mathematica","RSL","C","Matlab","Ruby","C++", + "Mercury","S","Caml","MetaPost","SAS","Clean", + "Miranda","Scilab","Cobol","Mizar","sh","Comal", + "ML","SHELXL","csh","Modula-2","Simula","Delphi", + "MuPAD","SQL","Eiffel","NASTRAN","tcl","Elan", + "Oberon-2","TeX","erlang","OCL","VBScript","Euphoria", + "Octave","Verilog","Fortran","Oz","VHDL","GCL", + "Pascal","VRML","Gnuplot","Perl","XML","Haskell", + "PHP","XSLT","HTML","PL/I"] + +-- Determine listings language from list of class attributes. +getListingsLanguage :: [String] -> Maybe String +getListingsLanguage [] = Nothing +getListingsLanguage (x:xs) = (if x `elem` listingsLangs + then Just x + else Nothing) <|> + M.lookup (map toLower x) langsMap <|> + getListingsLanguage xs diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index c481e6c87..d5e44e71a 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -17,9 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Writers.Man + Module : Text.Pandoc.Writers.Man Copyright : Copyright (C) 2007-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -32,6 +32,7 @@ module Text.Pandoc.Writers.Man ( writeMan) where import Text.Pandoc.Definition import Text.Pandoc.Templates import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.Readers.TeXMath import Text.Printf ( printf ) import Data.List ( isPrefixOf, intersperse, intercalate ) @@ -44,26 +45,25 @@ data WriterState = WriterState { stNotes :: Notes -- | Convert Pandoc to Man. writeMan :: WriterOptions -> Pandoc -> String -writeMan opts document = evalState (pandocToMan opts document) (WriterState [] False) +writeMan opts document = evalState (pandocToMan opts document) (WriterState [] False) -- | Return groff man representation of document. pandocToMan :: WriterOptions -> Pandoc -> State WriterState String pandocToMan opts (Pandoc (Meta title authors date) blocks) = do titleText <- inlineListToMan opts title authors' <- mapM (inlineListToMan opts) authors - date' <- inlineListToMan opts date + date' <- inlineListToMan opts date let colwidth = if writerWrapText opts then Just $ writerColumns opts else Nothing let render' = render colwidth let (cmdName, rest) = break (== ' ') $ render' titleText let (title', section) = case reverse cmdName of - (')':d:'(':xs) | d `elem` ['0'..'9'] -> + (')':d:'(':xs) | d `elem` ['0'..'9'] -> (text (reverse xs), char d) xs -> (text (reverse xs), doubleQuotes empty) let description = hsep $ - map (doubleQuotes . text . removeLeadingTrailingSpace) $ - splitBy (== '|') rest + map (doubleQuotes . text . trim) $ splitBy (== '|') rest body <- blockListToMan opts blocks notes <- liftM stNotes get notes' <- notesToMan opts (reverse notes) @@ -86,7 +86,7 @@ notesToMan :: WriterOptions -> [[Block]] -> State WriterState Doc notesToMan opts notes = if null notes then return empty - else mapM (\(num, note) -> noteToMan opts num note) (zip [1..] notes) >>= + else mapM (\(num, note) -> noteToMan opts num note) (zip [1..] notes) >>= return . (text ".SH NOTES" $$) . vcat -- | Return man representation of a note. @@ -94,7 +94,7 @@ noteToMan :: WriterOptions -> Int -> [Block] -> State WriterState Doc noteToMan opts num note = do contents <- blockListToMan opts note let marker = cr <> text ".SS " <> brackets (text (show num)) - return $ marker $$ contents + return $ marker $$ contents -- | Association list of characters to escape. manEscapes :: [(Char, String)] @@ -104,7 +104,7 @@ manEscapes = [ ('\160', "\\ ") , ('\x2014', "\\[em]") , ('\x2013', "\\[en]") , ('\x2026', "\\&...") - ] ++ backslashEscapes "@\\" + ] ++ backslashEscapes "-@\\" -- | Escape special characters for Man. escapeString :: String -> String @@ -113,7 +113,7 @@ escapeString = escapeStringUsing manEscapes -- | Escape a literal (code) section for Man. escapeCode :: String -> String escapeCode = concat . intersperse "\n" . map escapeLine . lines where - escapeLine codeline = + escapeLine codeline = case escapeStringUsing (manEscapes ++ backslashEscapes "\t ") codeline of a@('.':_) -> "\\&" ++ a b -> b @@ -150,23 +150,23 @@ splitSentences xs = -- | Convert Pandoc block element to man. blockToMan :: WriterOptions -- ^ Options -> Block -- ^ Block element - -> State WriterState Doc + -> State WriterState Doc blockToMan _ Null = return empty -blockToMan opts (Plain inlines) = +blockToMan opts (Plain inlines) = liftM vcat $ mapM (inlineListToMan opts) $ splitSentences inlines blockToMan opts (Para inlines) = do contents <- liftM vcat $ mapM (inlineListToMan opts) $ splitSentences inlines - return $ text ".PP" $$ contents + return $ text ".PP" $$ contents blockToMan _ (RawBlock "man" str) = return $ text str blockToMan _ (RawBlock _ _) = return empty blockToMan _ HorizontalRule = return $ text ".PP" $$ text " * * * * *" -blockToMan opts (Header level inlines) = do +blockToMan opts (Header level _ inlines) = do contents <- inlineListToMan opts inlines let heading = case level of 1 -> ".SH " _ -> ".SS " - return $ text heading <> contents + return $ text heading <> contents blockToMan _ (CodeBlock _ str) = return $ text ".IP" $$ text ".nf" $$ @@ -174,10 +174,10 @@ blockToMan _ (CodeBlock _ str) = return $ text (escapeCode str) $$ text "\\f[]" $$ text ".fi" -blockToMan opts (BlockQuote blocks) = do +blockToMan opts (BlockQuote blocks) = do contents <- blockListToMan opts blocks return $ text ".RS" $$ contents $$ text ".RE" -blockToMan opts (Table caption alignments widths headers rows) = +blockToMan opts (Table caption alignments widths headers rows) = let aligncode AlignLeft = "l" aligncode AlignRight = "r" aligncode AlignCenter = "c" @@ -190,53 +190,53 @@ blockToMan opts (Table caption alignments widths headers rows) = else map (printf "w(%0.2fn)" . (70 *)) widths -- 78n default width - 8n indent = 70n let coldescriptions = text $ intercalate " " - (zipWith (\align width -> aligncode align ++ width) + (zipWith (\align width -> aligncode align ++ width) alignments iwidths) ++ "." colheadings <- mapM (blockListToMan opts) headers - let makeRow cols = text "T{" $$ - (vcat $ intersperse (text "T}@T{") cols) $$ + let makeRow cols = text "T{" $$ + (vcat $ intersperse (text "T}@T{") cols) $$ text "T}" let colheadings' = if all null headers then empty else makeRow colheadings $$ char '_' - body <- mapM (\row -> do + body <- mapM (\row -> do cols <- mapM (blockListToMan opts) row return $ makeRow cols) rows - return $ text ".PP" $$ caption' $$ - text ".TS" $$ text "tab(@);" $$ coldescriptions $$ + return $ text ".PP" $$ caption' $$ + text ".TS" $$ text "tab(@);" $$ coldescriptions $$ colheadings' $$ vcat body $$ text ".TE" blockToMan opts (BulletList items) = do contents <- mapM (bulletListItemToMan opts) items - return (vcat contents) + return (vcat contents) blockToMan opts (OrderedList attribs items) = do - let markers = take (length items) $ orderedListMarkers attribs + let markers = take (length items) $ orderedListMarkers attribs let indent = 1 + (maximum $ map length markers) contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $ - zip markers items + zip markers items return (vcat contents) -blockToMan opts (DefinitionList items) = do +blockToMan opts (DefinitionList items) = do contents <- mapM (definitionListItemToMan opts) items return (vcat contents) -- | Convert bullet list item (list of blocks) to man. bulletListItemToMan :: WriterOptions -> [Block] -> State WriterState Doc bulletListItemToMan _ [] = return empty -bulletListItemToMan opts ((Para first):rest) = +bulletListItemToMan opts ((Para first):rest) = bulletListItemToMan opts ((Plain first):rest) bulletListItemToMan opts ((Plain first):rest) = do - first' <- blockToMan opts (Plain first) + first' <- blockToMan opts (Plain first) rest' <- blockListToMan opts rest let first'' = text ".IP \\[bu] 2" $$ first' let rest'' = if null rest then empty else text ".RS 2" $$ rest' $$ text ".RE" - return (first'' $$ rest'') + return (first'' $$ rest'') bulletListItemToMan opts (first:rest) = do first' <- blockToMan opts first rest' <- blockListToMan opts rest return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE" - + -- | Convert ordered list item (a list of blocks) to man. orderedListItemToMan :: WriterOptions -- ^ options -> String -- ^ order marker for list item @@ -244,7 +244,7 @@ orderedListItemToMan :: WriterOptions -- ^ options -> [Block] -- ^ list item (list of blocks) -> State WriterState Doc orderedListItemToMan _ _ _ [] = return empty -orderedListItemToMan opts num indent ((Para first):rest) = +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 @@ -254,17 +254,17 @@ orderedListItemToMan opts num indent (first:rest) = do let rest'' = if null rest then empty else text ".RS 4" $$ rest' $$ text ".RE" - return $ first'' $$ rest'' + return $ first'' $$ rest'' -- | Convert definition list item (label, list of blocks) to man. definitionListItemToMan :: WriterOptions - -> ([Inline],[[Block]]) + -> ([Inline],[[Block]]) -> State WriterState Doc definitionListItemToMan opts (label, defs) = do labelText <- inlineListToMan opts label - contents <- if null defs + contents <- if null defs then return empty - else liftM vcat $ forM defs $ \blocks -> do + else liftM vcat $ forM defs $ \blocks -> do let (first, rest) = case blocks of ((Para x):y) -> (Plain x,y) (x:y) -> (x,y) @@ -278,7 +278,7 @@ definitionListItemToMan opts (label, defs) = do -- | Convert list of Pandoc block elements to man. blockListToMan :: WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> State WriterState Doc + -> State WriterState Doc blockListToMan opts blocks = mapM (blockToMan opts) blocks >>= (return . vcat) @@ -292,7 +292,7 @@ inlineListToMan opts lst = mapM (inlineToMan opts) lst >>= (return . hcat) -- | Convert Pandoc inline element to man. inlineToMan :: WriterOptions -> Inline -> State WriterState Doc -inlineToMan opts (Emph lst) = do +inlineToMan opts (Emph lst) = do contents <- inlineListToMan opts lst return $ text "\\f[I]" <> contents <> text "\\f[]" inlineToMan opts (Strong lst) = do @@ -332,17 +332,18 @@ inlineToMan opts (Link txt (src, _)) = do linktext <- inlineListToMan opts txt let srcSuffix = if isPrefixOf "mailto:" src then drop 7 src else src return $ case txt of - [Code _ s] - | s == srcSuffix -> char '<' <> text srcSuffix <> char '>' + [Str s] + | escapeURI s == srcSuffix -> + char '<' <> text srcSuffix <> char '>' _ -> linktext <> text " (" <> text src <> char ')' inlineToMan opts (Image alternate (source, tit)) = do - let txt = if (null alternate) || (alternate == [Str ""]) || + let txt = if (null alternate) || (alternate == [Str ""]) || (alternate == [Str source]) -- to prevent autolinks then [Str "image"] else alternate - linkPart <- inlineToMan opts (Link txt (source, tit)) + linkPart <- inlineToMan opts (Link txt (source, tit)) return $ char '[' <> text "IMAGE: " <> linkPart <> char ']' -inlineToMan _ (Note contents) = do +inlineToMan _ (Note contents) = do -- add to notes in state modify $ \st -> st{ stNotes = contents : stNotes st } notes <- liftM stNotes get diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 9cbcaeb47..10d7d1ed2 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE OverloadedStrings, TupleSections #-} {- Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu> @@ -18,9 +18,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Writers.Markdown + Module : Text.Pandoc.Writers.Markdown Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -35,33 +35,42 @@ import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Shared -import Text.Pandoc.Parsing hiding (blankline) -import Text.ParserCombinators.Parsec ( runParser, GenParser ) +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (blankline, char, space) import Data.List ( group, isPrefixOf, find, intersperse, transpose ) import Text.Pandoc.Pretty import Control.Monad.State +import qualified Data.Set as Set +import Text.Pandoc.Writers.HTML (writeHtmlString) +import Text.Pandoc.Readers.TeXMath (readTeXMath) +import Text.HTML.TagSoup (renderTags, parseTags, isTagText, Tag(..)) +import Data.Default type Notes = [[Block]] type Refs = [([Inline], Target)] data WriterState = WriterState { stNotes :: Notes - , stRefs :: Refs + , stRefs :: Refs + , stIds :: [String] , stPlain :: Bool } +instance Default WriterState + where def = WriterState{ stNotes = [], stRefs = [], stIds = [], stPlain = False } -- | Convert Pandoc to Markdown. writeMarkdown :: WriterOptions -> Pandoc -> String -writeMarkdown opts document = - evalState (pandocToMarkdown opts document) WriterState{ stNotes = [] - , stRefs = [] - , stPlain = False } +writeMarkdown opts document = + evalState (pandocToMarkdown opts{ + writerWrapText = writerWrapText opts && + not (isEnabled Ext_hard_line_breaks opts) } + document) def -- | Convert Pandoc to plain text (like markdown, but without links, -- pictures, or inline formatting). writePlain :: WriterOptions -> Pandoc -> String writePlain opts document = - evalState (pandocToMarkdown opts{writerStrictMarkdown = True} - document') WriterState{ stNotes = [] - , stRefs = [] - , stPlain = True } + evalState (pandocToMarkdown opts{ + writerExtensions = Set.delete Ext_escaped_line_breaks $ + writerExtensions opts } + document') def{ stPlain = True } where document' = plainify document plainify :: Pandoc -> Pandoc @@ -81,15 +90,41 @@ plainify = bottomUp go go (Cite _ cits) = SmallCaps cits go x = x +pandocTitleBlock :: Doc -> [Doc] -> Doc -> Doc +pandocTitleBlock tit auths dat = + hang 2 (text "% ") tit <> cr <> + hang 2 (text "% ") (hcat (intersperse (text "; ") auths)) <> cr <> + hang 2 (text "% ") dat <> cr + +mmdTitleBlock :: Doc -> [Doc] -> Doc -> Doc +mmdTitleBlock tit auths dat = + hang 8 (text "Title: ") tit <> cr <> + hang 8 (text "Author: ") (hcat (intersperse (text "; ") auths)) <> cr <> + hang 8 (text "Date: ") dat <> cr + +plainTitleBlock :: Doc -> [Doc] -> Doc -> Doc +plainTitleBlock tit auths dat = + tit <> cr <> + (hcat (intersperse (text "; ") auths)) <> cr <> + dat <> cr + -- | Return markdown representation of document. pandocToMarkdown :: WriterOptions -> Pandoc -> State WriterState String pandocToMarkdown opts (Pandoc (Meta title authors date) blocks) = do title' <- inlineListToMarkdown opts title authors' <- mapM (inlineListToMarkdown opts) authors date' <- inlineListToMarkdown opts date - let titleblock = not $ null title && null authors && null date + isPlain <- gets stPlain + let titleblock = case True of + _ | isPlain -> + plainTitleBlock title' authors' date' + | isEnabled Ext_pandoc_title_block opts -> + pandocTitleBlock title' authors' date' + | isEnabled Ext_mmd_title_block opts -> + mmdTitleBlock title' authors' date' + | otherwise -> empty let headerBlocks = filter isHeaderBlock blocks - let toc = if writerTableOfContents opts + let toc = if writerTableOfContents opts then tableOfContents opts headerBlocks else empty body <- blockListToMarkdown opts blocks @@ -106,11 +141,9 @@ pandocToMarkdown opts (Pandoc (Meta title authors date) blocks) = do let context = writerVariables opts ++ [ ("toc", render colwidth toc) , ("body", main) - , ("title", render colwidth title') - , ("date", render colwidth date') ] ++ - [ ("titleblock", "yes") | titleblock ] ++ - [ ("author", render colwidth a) | a <- authors' ] + [ ("titleblock", render colwidth titleblock) + | not (null title && null authors && null date) ] if writerStandalone opts then return $ renderTemplate context $ writerTemplate opts else return main @@ -119,9 +152,9 @@ pandocToMarkdown opts (Pandoc (Meta title authors date) blocks) = do refsToMarkdown :: WriterOptions -> Refs -> State WriterState Doc refsToMarkdown opts refs = mapM (keyToMarkdown opts) refs >>= return . vcat --- | Return markdown representation of a reference key. -keyToMarkdown :: WriterOptions - -> ([Inline], (String, String)) +-- | Return markdown representation of a reference key. +keyToMarkdown :: WriterOptions + -> ([Inline], (String, String)) -> State WriterState Doc keyToMarkdown opts (label, (src, tit)) = do label' <- inlineListToMarkdown opts label @@ -133,7 +166,7 @@ keyToMarkdown opts (label, (src, tit)) = do -- | Return markdown representation of notes. notesToMarkdown :: WriterOptions -> [[Block]] -> State WriterState Doc -notesToMarkdown opts notes = +notesToMarkdown opts notes = mapM (\(num, note) -> noteToMarkdown opts num note) (zip [1..] notes) >>= return . vsep @@ -141,13 +174,17 @@ notesToMarkdown opts notes = noteToMarkdown :: WriterOptions -> Int -> [Block] -> State WriterState Doc noteToMarkdown opts num blocks = do contents <- blockListToMarkdown opts blocks - let num' = text $ show num - let marker = text "[^" <> num' <> text "]:" + let num' = text $ writerIdentifierPrefix opts ++ show num + let marker = if isEnabled Ext_footnotes opts + then text "[^" <> num' <> text "]:" + else text "[" <> num' <> text "]" let markerSize = 4 + offset num' let spacer = case writerTabStop opts - markerSize of n | n > 0 -> text $ replicate n ' ' _ -> text " " - return $ hang (writerTabStop opts) (marker <> spacer) contents + return $ if isEnabled Ext_footnotes opts + then hang (writerTabStop opts) (marker <> spacer) contents + else marker <> spacer <> contents -- | Escape special characters for Markdown. escapeString :: String -> String @@ -155,21 +192,19 @@ escapeString = escapeStringUsing markdownEscapes where markdownEscapes = backslashEscapes "\\`*_$<>#~^" -- | Construct table of contents from list of header blocks. -tableOfContents :: WriterOptions -> [Block] -> Doc +tableOfContents :: WriterOptions -> [Block] -> Doc tableOfContents opts headers = let opts' = opts { writerIgnoreNotes = True } - contents = BulletList $ map elementToListItem $ hierarchicalize headers - in evalState (blockToMarkdown opts' contents) WriterState{ stNotes = [] - , stRefs = [] - , stPlain = False } + contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers + in evalState (blockToMarkdown opts' contents) def -- | Converts an Element to a list item for a table of contents, -elementToListItem :: Element -> [Block] -elementToListItem (Blk _) = [] -elementToListItem (Sec _ _ _ headerText subsecs) = [Plain headerText] ++ - if null subsecs - then [] - else [BulletList $ map elementToListItem subsecs] +elementToListItem :: WriterOptions -> Element -> [Block] +elementToListItem opts (Sec lev _ _ headerText subsecs) + = Plain headerText : + [ BulletList (map (elementToListItem opts) subsecs) | + not (null subsecs) && lev < writerTOCDepth opts ] +elementToListItem _ (Blk _) = [] attrsToMarkdown :: Attr -> Doc attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] @@ -188,9 +223,9 @@ attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] <> "=\"" <> text v <> "\"") ks -- | Ordered list start parser for use in Para below. -olMarker :: GenParser Char ParserState Char +olMarker :: Parser [Char] ParserState Char olMarker = do (start, style', delim) <- anyOrderedListMarker - if delim == Period && + if delim == Period && (style' == UpperAlpha || (style' == UpperRoman && start `elem` [1, 5, 10, 50, 100, 500, 1000])) then spaceChar >> spaceChar @@ -206,23 +241,33 @@ beginsWithOrderedListMarker str = -- | Convert Pandoc block element to markdown. blockToMarkdown :: WriterOptions -- ^ Options -> Block -- ^ Block element - -> State WriterState Doc + -> State WriterState Doc blockToMarkdown _ Null = return empty blockToMarkdown opts (Plain inlines) = do contents <- inlineListToMarkdown opts inlines return $ contents <> cr +-- title beginning with fig: indicates figure +blockToMarkdown opts (Para [Image alt (src,'f':'i':'g':':':tit)]) = + blockToMarkdown opts (Para [Image alt (src,tit)]) blockToMarkdown opts (Para inlines) = do contents <- inlineListToMarkdown opts inlines -- escape if para starts with ordered list marker st <- get - let esc = if (not (writerStrictMarkdown opts)) && + let esc = if isEnabled Ext_all_symbols_escapable opts && not (stPlain st) && beginsWithOrderedListMarker (render Nothing contents) then text "\x200B" -- zero-width space, a hack else empty return $ esc <> contents <> blankline -blockToMarkdown _ (RawBlock f str) - | f == "html" || f == "latex" || f == "tex" || f == "markdown" = do +blockToMarkdown opts (RawBlock f str) + | f == "html" = do + st <- get + if stPlain st + then return empty + else return $ if isEnabled Ext_markdown_attribute opts + then text (addMarkdownAttribute str) <> text "\n" + else text str <> text "\n" + | f == "latex" || f == "tex" || f == "markdown" = do st <- get if stPlain st then return empty @@ -230,101 +275,175 @@ blockToMarkdown _ (RawBlock f str) blockToMarkdown _ (RawBlock _ _) = return empty blockToMarkdown _ HorizontalRule = return $ blankline <> text "* * * * *" <> blankline -blockToMarkdown opts (Header level inlines) = do +blockToMarkdown opts (Header level attr inlines) = do + -- we calculate the id that would be used by auto_identifiers + -- so we know whether to print an explicit identifier + ids <- gets stIds + let autoId = uniqueIdent inlines ids + modify $ \st -> st{ stIds = autoId : ids } + let attr' = case attr of + ("",[],[]) -> empty + (id',[],[]) | isEnabled Ext_auto_identifiers opts + && id' == autoId -> empty + (id',_,_) | isEnabled Ext_mmd_header_identifiers opts -> + space <> brackets (text id') + _ | isEnabled Ext_header_attributes opts -> + space <> attrsToMarkdown attr + | otherwise -> empty contents <- inlineListToMarkdown opts inlines st <- get let setext = writerSetextHeaders opts return $ nowrap $ case level of 1 | setext -> - contents <> cr <> text (replicate (offset contents) '=') <> + contents <> attr' <> cr <> text (replicate (offset contents) '=') <> blankline 2 | setext -> - contents <> cr <> text (replicate (offset contents) '-') <> + contents <> attr' <> cr <> text (replicate (offset contents) '-') <> blankline -- ghc interprets '#' characters in column 1 as linenum specifiers. - _ | stPlain st || writerLiterateHaskell opts -> + _ | stPlain st || isEnabled Ext_literate_haskell opts -> contents <> blankline - _ -> text (replicate level '#') <> space <> contents <> blankline + _ -> text (replicate level '#') <> space <> contents <> attr' <> blankline blockToMarkdown opts (CodeBlock (_,classes,_) str) | "haskell" `elem` classes && "literate" `elem` classes && - writerLiterateHaskell opts = + isEnabled Ext_literate_haskell opts = return $ prefixed "> " (text str) <> blankline blockToMarkdown opts (CodeBlock attribs str) = return $ - if writerStrictMarkdown opts || attribs == nullAttr - then nest (writerTabStop opts) (text str) <> blankline - else -- use delimited code block - (tildes <> space <> attrs <> cr <> text str <> - cr <> tildes) <> blankline - where tildes = text "~~~~" - attrs = attrsToMarkdown attribs + case attribs of + x | x /= nullAttr && isEnabled Ext_fenced_code_blocks opts -> + tildes <> space <> attrs <> cr <> text str <> + cr <> tildes <> blankline + (_,(cls:_),_) | isEnabled Ext_backtick_code_blocks opts -> + backticks <> space <> text cls <> cr <> text str <> + cr <> backticks <> blankline + _ -> nest (writerTabStop opts) (text str) <> blankline + where tildes = text $ case [ln | ln <- lines str, all (=='~') ln] of + [] -> "~~~~" + xs -> case maximum $ map length xs of + n | n < 3 -> "~~~~" + | otherwise -> replicate (n+1) '~' + backticks = text "```" + attrs = if isEnabled Ext_fenced_code_attributes opts + then attrsToMarkdown attribs + else empty blockToMarkdown opts (BlockQuote blocks) = do st <- get -- if we're writing literate haskell, put a space before the bird tracks -- so they won't be interpreted as lhs... - let leader = if writerLiterateHaskell opts + let leader = if isEnabled Ext_literate_haskell opts then " > " else if stPlain st then " " else "> " contents <- blockListToMarkdown opts blocks return $ (prefixed leader contents) <> blankline -blockToMarkdown opts (Table caption aligns widths headers rows) = do +blockToMarkdown opts t@(Table caption aligns widths headers rows) = do caption' <- inlineListToMarkdown opts caption - let caption'' = if null caption + let caption'' = if null caption || not (isEnabled Ext_table_captions opts) then empty else blankline <> ": " <> caption' <> blankline - headers' <- mapM (blockListToMarkdown opts) headers + rawHeaders <- mapM (blockListToMarkdown opts) headers + rawRows <- mapM (mapM (blockListToMarkdown opts)) rows + let isSimple = all (==0) widths + (nst,tbl) <- case isSimple of + True | isEnabled Ext_simple_tables opts -> fmap (nest 2,) $ + pandocTable opts (all null headers) aligns widths + rawHeaders rawRows + | isEnabled Ext_pipe_tables opts -> fmap (id,) $ + pipeTable (all null headers) aligns rawHeaders rawRows + | otherwise -> fmap (id,) $ + return $ text $ writeHtmlString def + $ Pandoc (Meta [] [] []) [t] + False | isEnabled Ext_multiline_tables opts -> fmap (nest 2,) $ + pandocTable opts (all null headers) aligns widths + rawHeaders rawRows + | otherwise -> fmap (id,) $ + return $ text $ writeHtmlString def + $ Pandoc (Meta [] [] []) [t] + return $ nst $ tbl $$ blankline $$ caption'' $$ blankline +blockToMarkdown opts (BulletList items) = do + contents <- mapM (bulletListItemToMarkdown opts) items + return $ cat contents <> blankline +blockToMarkdown opts (OrderedList (start,sty,delim) items) = do + let start' = if isEnabled Ext_startnum opts then start else 1 + let sty' = if isEnabled Ext_fancy_lists opts then sty else DefaultStyle + let delim' = if isEnabled Ext_fancy_lists opts then delim else DefaultDelim + let attribs = (start', sty', delim') + let markers = orderedListMarkers attribs + let markers' = map (\m -> if length m < 3 + then m ++ replicate (3 - length m) ' ' + else m) markers + contents <- mapM (\(item, num) -> orderedListItemToMarkdown opts item num) $ + zip markers' items + return $ cat contents <> blankline +blockToMarkdown opts (DefinitionList items) = do + contents <- mapM (definitionListItemToMarkdown opts) items + return $ cat contents <> blankline + +addMarkdownAttribute :: String -> String +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 + +pipeTable :: Bool -> [Alignment] -> [Doc] -> [[Doc]] -> State WriterState Doc +pipeTable headless aligns rawHeaders rawRows = do + let torow cs = nowrap $ text "|" <> + hcat (intersperse (text "|") $ map chomp cs) <> text "|" + let toborder (a, h) = let wid = max (offset h) 3 + in text $ case a of + AlignLeft -> ':':replicate (wid - 1) '-' + AlignCenter -> ':':replicate (wid - 2) '-' ++ ":" + AlignRight -> replicate (wid - 1) '-' ++ ":" + AlignDefault -> replicate wid '-' + let header = if headless then empty else torow rawHeaders + let border = torow $ map toborder $ zip aligns rawHeaders + let body = vcat $ map torow rawRows + return $ header $$ border $$ body + +pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double] + -> [Doc] -> [[Doc]] -> State WriterState Doc +pandocTable opts 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 - rawRows <- mapM (mapM (blockListToMarkdown opts)) rows - let isSimple = all (==0) widths let numChars = maximum . map offset - let widthsInChars = - if isSimple - then map ((+2) . numChars) $ transpose (headers' : rawRows) - else map (floor . (fromIntegral (writerColumns opts) *)) widths + let widthsInChars = if isSimple + then map ((+2) . numChars) + $ transpose (rawHeaders : rawRows) + else map + (floor . (fromIntegral (writerColumns opts) *)) + widths let makeRow = hcat . intersperse (lblock 1 (text " ")) . (zipWith3 alignHeader aligns widthsInChars) let rows' = map makeRow rawRows - let head' = makeRow headers' + let head' = makeRow rawHeaders let maxRowHeight = maximum $ map height (head':rows') let underline = cat $ intersperse (text " ") $ map (\width -> text (replicate width '-')) widthsInChars let border = if maxRowHeight > 1 then text (replicate (sum widthsInChars + length widthsInChars - 1) '-') - else if all null headers + else if headless then underline else empty - let head'' = if all null headers + let head'' = if headless then empty else border <> cr <> head' let body = if maxRowHeight > 1 then vsep rows' else vcat rows' - let bottom = if all null headers + let bottom = if headless then underline else border - return $ nest 2 $ head'' $$ underline $$ body $$ - bottom $$ blankline $$ caption'' $$ blankline -blockToMarkdown opts (BulletList items) = do - contents <- mapM (bulletListItemToMarkdown opts) items - return $ cat contents <> blankline -blockToMarkdown opts (OrderedList attribs items) = do - let markers = orderedListMarkers attribs - let markers' = map (\m -> if length m < 3 - then m ++ replicate (3 - length m) ' ' - else m) markers - contents <- mapM (\(item, num) -> orderedListItemToMarkdown opts item num) $ - zip markers' items - return $ cat contents <> blankline -blockToMarkdown opts (DefinitionList items) = do - contents <- mapM (definitionListItemToMarkdown opts) items - return $ cat contents <> blankline + return $ head'' $$ underline $$ body $$ bottom -- | Convert bullet list item (list of blocks) to markdown. bulletListItemToMarkdown :: WriterOptions -> [Block] -> State WriterState Doc @@ -349,32 +468,38 @@ orderedListItemToMarkdown opts marker items = do -- | Convert definition list item (label, list of blocks) to markdown. definitionListItemToMarkdown :: WriterOptions - -> ([Inline],[[Block]]) + -> ([Inline],[[Block]]) -> State WriterState Doc definitionListItemToMarkdown opts (label, defs) = do labelText <- inlineListToMarkdown opts label - let tabStop = writerTabStop opts - st <- get - let leader = if stPlain st then " " else ": " - let sps = case writerTabStop opts - 3 of - n | n > 0 -> text $ replicate n ' ' - _ -> text " " defs' <- mapM (mapM (blockToMarkdown opts)) defs - let contents = vcat $ map (\d -> hang tabStop (leader <> sps) $ vcat d <> cr) defs' - return $ nowrap labelText <> cr <> contents <> cr + if isEnabled Ext_definition_lists opts + then do + let tabStop = writerTabStop opts + st <- get + let leader = if stPlain st then " " else ": " + let sps = case writerTabStop opts - 3 of + n | n > 0 -> text $ replicate n ' ' + _ -> text " " + let contents = vcat $ map (\d -> hang tabStop (leader <> sps) $ vcat d <> cr) defs' + return $ nowrap labelText <> cr <> contents <> cr + else do + return $ nowrap labelText <> text " " <> cr <> + vsep (map vsep defs') <> blankline -- | Convert list of Pandoc block elements to markdown. blockListToMarkdown :: WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> State WriterState Doc + -> State WriterState Doc blockListToMarkdown opts blocks = mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat -- insert comment between list and indented code block, or the -- code block will be treated as a list continuation paragraph where fixBlocks (b : CodeBlock attr x : rest) - | (writerStrictMarkdown opts || attr == nullAttr) && isListBlock b = + | (not (isEnabled Ext_fenced_code_blocks opts) || attr == nullAttr) + && isListBlock b = b : RawBlock "html" "<!-- -->\n" : CodeBlock attr x : - fixBlocks rest + fixBlocks rest fixBlocks (x : xs) = x : fixBlocks xs fixBlocks [] = [] isListBlock (BulletList _) = True @@ -412,7 +537,7 @@ escapeSpaces x = x -- | Convert Pandoc inline element to markdown. inlineToMarkdown :: WriterOptions -> Inline -> State WriterState Doc -inlineToMarkdown opts (Emph lst) = do +inlineToMarkdown opts (Emph lst) = do contents <- inlineListToMarkdown opts lst return $ "*" <> contents <> "*" inlineToMarkdown opts (Strong lst) = do @@ -420,15 +545,21 @@ inlineToMarkdown opts (Strong lst) = do return $ "**" <> contents <> "**" inlineToMarkdown opts (Strikeout lst) = do contents <- inlineListToMarkdown opts lst - return $ "~~" <> contents <> "~~" + return $ if isEnabled Ext_strikeout opts + then "~~" <> contents <> "~~" + else "<s>" <> contents <> "</s>" inlineToMarkdown opts (Superscript lst) = do let lst' = bottomUp escapeSpaces lst contents <- inlineListToMarkdown opts lst' - return $ "^" <> contents <> "^" + return $ if isEnabled Ext_superscript opts + then "^" <> contents <> "^" + else "<sup>" <> contents <> "</sup>" inlineToMarkdown opts (Subscript lst) = do let lst' = bottomUp escapeSpaces lst contents <- inlineListToMarkdown opts lst' - return $ "~" <> contents <> "~" + return $ if isEnabled Ext_subscript opts + then "~" <> contents <> "~" + else "<sub>" <> contents <> "</sub>" inlineToMarkdown opts (SmallCaps lst) = inlineListToMarkdown opts lst inlineToMarkdown opts (Quoted SingleQuote lst) = do contents <- inlineListToMarkdown opts lst @@ -437,33 +568,47 @@ inlineToMarkdown opts (Quoted DoubleQuote lst) = do contents <- inlineListToMarkdown opts lst return $ "“" <> contents <> "”" inlineToMarkdown opts (Code attr str) = - let tickGroups = filter (\s -> '`' `elem` s) $ group str + let tickGroups = filter (\s -> '`' `elem` s) $ group str longest = if null tickGroups then 0 - else maximum $ map length tickGroups - marker = replicate (longest + 1) '`' + else maximum $ map length tickGroups + marker = replicate (longest + 1) '`' spacer = if (longest == 0) then "" else " " - attrs = if writerStrictMarkdown opts || attr == nullAttr - then empty - else attrsToMarkdown attr + attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr + then attrsToMarkdown attr + else empty in return $ text (marker ++ spacer ++ str ++ spacer ++ marker) <> attrs inlineToMarkdown _ (Str str) = do st <- get if stPlain st then return $ text str else return $ text $ escapeString str -inlineToMarkdown _ (Math InlineMath str) = - return $ "$" <> text str <> "$" -inlineToMarkdown _ (Math DisplayMath str) = - return $ "$$" <> text str <> "$$" -inlineToMarkdown _ (RawInline f str) - | f == "html" || f == "latex" || f == "tex" || f == "markdown" = +inlineToMarkdown opts (Math InlineMath str) + | isEnabled Ext_tex_math_dollars opts = + return $ "$" <> text str <> "$" + | isEnabled Ext_tex_math_single_backslash opts = + return $ "\\(" <> text str <> "\\)" + | isEnabled Ext_tex_math_double_backslash opts = + return $ "\\\\(" <> text str <> "\\\\)" + | otherwise = inlineListToMarkdown opts $ readTeXMath str +inlineToMarkdown opts (Math DisplayMath str) + | isEnabled Ext_tex_math_dollars opts = + return $ "$$" <> text str <> "$$" + | isEnabled Ext_tex_math_single_backslash opts = + return $ "\\[" <> text str <> "\\]" + | isEnabled Ext_tex_math_double_backslash opts = + return $ "\\\\[" <> text str <> "\\\\]" + | otherwise = (\x -> cr <> x <> cr) `fmap` + inlineListToMarkdown opts (readTeXMath str) +inlineToMarkdown opts (RawInline f str) + | f == "html" || f == "markdown" || + (isEnabled Ext_raw_tex opts && (f == "latex" || f == "tex")) = return $ text str inlineToMarkdown _ (RawInline _ _) = return empty -inlineToMarkdown opts (LineBreak) = return $ - if writerStrictMarkdown opts - then " " <> cr - else "\\" <> cr +inlineToMarkdown opts (LineBreak) + | isEnabled Ext_hard_line_breaks opts = return cr + | isEnabled Ext_escaped_line_breaks opts = return $ "\\" <> cr + | otherwise = return $ " " <> cr inlineToMarkdown _ Space = return space inlineToMarkdown opts (Cite (c:cs) lst) | writerCiteMethod opts == Citeproc = inlineListToMarkdown opts lst @@ -500,8 +645,8 @@ inlineToMarkdown opts (Link txt (src, tit)) = do else text $ " \"" ++ tit ++ "\"" let srcSuffix = if isPrefixOf "mailto:" src then drop 7 src else src let useAuto = case (tit,txt) of - ("", [Code _ s]) | s == srcSuffix -> True - _ -> False + ("", [Str s]) | escapeURI s == srcSuffix -> True + _ -> False let useRefLinks = writerReferenceLinks opts && not useAuto ref <- if useRefLinks then getReference txt (src, tit) else return [] reftext <- inlineListToMarkdown opts ref @@ -513,7 +658,7 @@ inlineToMarkdown opts (Link txt (src, tit)) = do then "[]" else "[" <> reftext <> "]" in first <> second - else "[" <> linktext <> "](" <> + else "[" <> linktext <> "](" <> text src <> linktitle <> ")" inlineToMarkdown opts (Image alternate (source, tit)) = do let txt = if null alternate || alternate == [Str source] @@ -522,8 +667,10 @@ inlineToMarkdown opts (Image alternate (source, tit)) = do else alternate linkPart <- inlineToMarkdown opts (Link txt (source, tit)) return $ "!" <> linkPart -inlineToMarkdown _ (Note contents) = do +inlineToMarkdown opts (Note contents) = do modify (\st -> st{ stNotes = contents : stNotes st }) st <- get - let ref = show $ (length $ stNotes st) - return $ "[^" <> text ref <> "]" + let ref = text $ writerIdentifierPrefix opts ++ show (length $ stNotes st) + if isEnabled Ext_footnotes opts + then return $ "[^" <> ref <> "]" + else return $ "[" <> ref <> "]" diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index b32c5327d..4cec2d648 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -17,9 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Writers.MediaWiki + Module : Text.Pandoc.Writers.MediaWiki Copyright : Copyright (C) 2008-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -31,7 +31,8 @@ MediaWiki: <http://www.mediawiki.org/wiki/MediaWiki> -} module Text.Pandoc.Writers.MediaWiki ( writeMediaWiki ) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Options +import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.XML ( escapeStringForXML ) import Data.List ( intersect, intercalate ) @@ -46,9 +47,9 @@ data WriterState = WriterState { -- | Convert Pandoc to MediaWiki. writeMediaWiki :: WriterOptions -> Pandoc -> String -writeMediaWiki opts document = - evalState (pandocToMediaWiki opts document) - (WriterState { stNotes = False, stListLevel = [], stUseTags = False }) +writeMediaWiki opts document = + evalState (pandocToMediaWiki opts document) + (WriterState { stNotes = False, stListLevel = [], stUseTags = False }) -- | Return MediaWiki representation of document. pandocToMediaWiki :: WriterOptions -> Pandoc -> State WriterState String @@ -57,7 +58,7 @@ pandocToMediaWiki opts (Pandoc _ blocks) = do notesExist <- get >>= return . stNotes let notes = if notesExist then "\n<references />" - else "" + else "" let main = body ++ notes let context = writerVariables opts ++ [ ("body", main) ] ++ @@ -70,22 +71,24 @@ pandocToMediaWiki opts (Pandoc _ blocks) = do escapeString :: String -> String escapeString = escapeStringForXML --- | Convert Pandoc block element to MediaWiki. +-- | Convert Pandoc block element to MediaWiki. blockToMediaWiki :: WriterOptions -- ^ Options -> Block -- ^ Block element - -> State WriterState String + -> State WriterState String blockToMediaWiki _ Null = return "" -blockToMediaWiki opts (Plain inlines) = +blockToMediaWiki opts (Plain inlines) = inlineListToMediaWiki opts inlines -blockToMediaWiki opts (Para [Image txt (src,tit)]) = do - capt <- inlineListToMediaWiki opts txt +-- title beginning with fig: indicates that the image is a figure +blockToMediaWiki opts (Para [Image txt (src,'f':'i':'g':':':tit)]) = do + capt <- if null txt + then return "" + else ("|caption " ++) `fmap` inlineListToMediaWiki opts txt let opt = if null txt then "" - else "|alt=" ++ if null tit then capt else tit ++ - "|caption " ++ capt + else "|alt=" ++ if null tit then capt else tit ++ capt return $ "[[Image:" ++ src ++ "|frame|none" ++ opt ++ "]]\n" blockToMediaWiki opts (Para inlines) = do @@ -102,7 +105,7 @@ blockToMediaWiki _ (RawBlock _ _) = return "" blockToMediaWiki _ HorizontalRule = return "\n-----\n" -blockToMediaWiki opts (Header level inlines) = do +blockToMediaWiki opts (Header level _ inlines) = do contents <- inlineListToMediaWiki opts inlines let eqs = replicate level '=' return $ eqs ++ " " ++ contents ++ " " ++ eqs ++ "\n" @@ -115,7 +118,7 @@ blockToMediaWiki _ (CodeBlock (_,classes,_) str) = do "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas", "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", - "smalltalk", "smarty", "sql", "tcl", "", "thinbasic", "tsql", "vb", "vbnet", "vhdl", + "smalltalk", "smarty", "sql", "tcl", "", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80"] let (beg, end) = if null at then ("<pre" ++ if null classes then ">" else " class=\"" ++ unwords classes ++ "\">", "</pre>") @@ -124,7 +127,7 @@ blockToMediaWiki _ (CodeBlock (_,classes,_) str) = do blockToMediaWiki opts (BlockQuote blocks) = do contents <- blockListToMediaWiki opts blocks - return $ "<blockquote>" ++ contents ++ "</blockquote>" + return $ "<blockquote>" ++ contents ++ "</blockquote>" blockToMediaWiki opts (Table capt aligns widths headers rows') = do let alignStrings = map alignmentToString aligns @@ -221,7 +224,7 @@ listItemToMediaWiki opts items = do -- | Convert definition list item (label, list of blocks) to MediaWiki. definitionListItemToMediaWiki :: WriterOptions - -> ([Inline],[[Block]]) + -> ([Inline],[[Block]]) -> State WriterState String definitionListItemToMediaWiki opts (label, items) = do labelText <- inlineListToMediaWiki opts label @@ -242,7 +245,7 @@ isSimpleList x = 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 + DefinitionList items -> all isSimpleListItem $ concatMap snd items _ -> False -- | True if list item can be handled with the simple wiki syntax. False if @@ -287,8 +290,8 @@ tableRowToMediaWiki opts alignStrings rownum cols' = do 0 -> "header" x | x `rem` 2 == 1 -> "odd" _ -> "even" - cols'' <- sequence $ zipWith - (\alignment item -> tableItemToMediaWiki opts celltype alignment item) + cols'' <- sequence $ zipWith + (\alignment item -> tableItemToMediaWiki opts celltype alignment item) alignStrings cols' return $ "<tr class=\"" ++ rowclass ++ "\">\n" ++ unlines cols'' ++ "</tr>" @@ -313,7 +316,7 @@ tableItemToMediaWiki opts celltype align' item = do -- | Convert list of Pandoc block elements to MediaWiki. blockListToMediaWiki :: WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> State WriterState String + -> State WriterState String blockListToMediaWiki opts blocks = mapM (blockToMediaWiki opts) blocks >>= return . vcat @@ -325,9 +328,9 @@ inlineListToMediaWiki opts lst = -- | Convert Pandoc inline element to MediaWiki. inlineToMediaWiki :: WriterOptions -> Inline -> State WriterState String -inlineToMediaWiki opts (Emph lst) = do +inlineToMediaWiki opts (Emph lst) = do contents <- inlineListToMediaWiki opts lst - return $ "''" ++ contents ++ "''" + return $ "''" ++ contents ++ "''" inlineToMediaWiki opts (Strong lst) = do contents <- inlineListToMediaWiki opts lst @@ -358,25 +361,25 @@ inlineToMediaWiki opts (Quoted DoubleQuote lst) = do inlineToMediaWiki opts (Cite _ lst) = inlineListToMediaWiki opts lst inlineToMediaWiki _ (Code _ str) = - return $ "<tt>" ++ (escapeString str) ++ "</tt>" + return $ "<code>" ++ (escapeString str) ++ "</code>" inlineToMediaWiki _ (Str str) = return $ escapeString str inlineToMediaWiki _ (Math _ str) = return $ "<math>" ++ str ++ "</math>" -- note: str should NOT be escaped -inlineToMediaWiki _ (RawInline "mediawiki" str) = return str -inlineToMediaWiki _ (RawInline "html" str) = return str +inlineToMediaWiki _ (RawInline "mediawiki" str) = return str +inlineToMediaWiki _ (RawInline "html" str) = return str inlineToMediaWiki _ (RawInline _ _) = return "" -inlineToMediaWiki _ (LineBreak) = return "<br />\n" +inlineToMediaWiki _ (LineBreak) = return "<br />" inlineToMediaWiki _ Space = return " " inlineToMediaWiki opts (Link txt (src, _)) = do label <- inlineListToMediaWiki opts txt case txt of - [Code _ s] | s == src -> return src + [Str s] | escapeURI s == src -> return src _ -> if isURI src then return $ "[" ++ src ++ " " ++ label ++ "]" else return $ "[[" ++ src' ++ "|" ++ label ++ "]]" @@ -392,7 +395,7 @@ inlineToMediaWiki opts (Image alt (source, tit)) = do else "|" ++ tit return $ "[[Image:" ++ source ++ txt ++ "]]" -inlineToMediaWiki opts (Note contents) = do +inlineToMediaWiki opts (Note contents) = do contents' <- blockListToMediaWiki opts contents modify (\s -> s { stNotes = True }) return $ "<ref>" ++ contents' ++ "</ref>" diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index d2b56cd17..7fb304e86 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Native Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -34,7 +34,7 @@ metadata. -} module Text.Pandoc.Writers.Native ( writeNative ) where -import Text.Pandoc.Shared ( WriterOptions(..) ) +import Text.Pandoc.Options ( WriterOptions(..) ) import Data.List ( intersperse ) import Text.Pandoc.Definition import Text.Pandoc.Pretty @@ -47,17 +47,17 @@ prettyList ds = prettyBlock :: Block -> Doc prettyBlock (BlockQuote blocks) = "BlockQuote" $$ prettyList (map prettyBlock blocks) -prettyBlock (OrderedList attribs blockLists) = +prettyBlock (OrderedList attribs blockLists) = "OrderedList" <> space <> text (show attribs) $$ (prettyList $ map (prettyList . map prettyBlock) blockLists) -prettyBlock (BulletList blockLists) = +prettyBlock (BulletList blockLists) = "BulletList" $$ (prettyList $ map (prettyList . map prettyBlock) blockLists) prettyBlock (DefinitionList items) = "DefinitionList" $$ (prettyList $ map deflistitem items) where deflistitem (term, defs) = "(" <> text (show term) <> "," <> cr <> nest 1 (prettyList $ map (prettyList . map prettyBlock) defs) <> ")" -prettyBlock (Table caption aligns widths header rows) = +prettyBlock (Table caption aligns widths header rows) = "Table " <> text (show caption) <> " " <> text (show aligns) <> " " <> text (show widths) $$ prettyRow header $$ diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 6f5387691..dac718ac8 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2008-2010 John MacFarlane <jgm@berkeley.edu> @@ -27,55 +28,45 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to ODT. -} -{-# LANGUAGE ScopedTypeVariables #-} module Text.Pandoc.Writers.ODT ( writeODT ) where import Data.IORef import Data.List ( isPrefixOf ) -import System.FilePath ( (</>), takeExtension ) import qualified Data.ByteString.Lazy as B -import Data.ByteString.Lazy.UTF8 ( fromString ) +import Text.Pandoc.UTF8 ( fromStringLazy ) import Codec.Archive.Zip -import Data.Time.Clock.POSIX -import Paths_pandoc ( getDataFileName ) -import Text.Pandoc.Shared ( WriterOptions(..) ) -import Text.Pandoc.ImageSize ( readImageSize, sizeInPoints ) +import Text.Pandoc.Options ( WriterOptions(..) ) +import Text.Pandoc.Shared ( stringify, readDataFile, fetchItem, warn ) +import Text.Pandoc.ImageSize ( imageSize, sizeInPoints ) import Text.Pandoc.MIME ( getMimeType ) import Text.Pandoc.Definition import Text.Pandoc.Generic import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) -import System.Directory import Control.Monad (liftM) -import Network.URI ( unEscapeString ) +import Control.Monad.Trans (liftIO) import Text.Pandoc.XML import Text.Pandoc.Pretty -import qualified Control.Exception as E (catch, IOException) +import qualified Control.Exception as E +import Data.Time.Clock.POSIX ( getPOSIXTime ) +import System.FilePath ( takeExtension ) -- | Produce an ODT file from a Pandoc document. -writeODT :: Maybe FilePath -- ^ Path specified by --reference-odt - -> WriterOptions -- ^ Writer options +writeODT :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO B.ByteString -writeODT mbRefOdt opts doc = do +writeODT opts doc@(Pandoc (Meta title _ _) _) = do let datadir = writerUserDataDir opts refArchive <- liftM toArchive $ - case mbRefOdt of + case writerReferenceODT opts of Just f -> B.readFile f - Nothing -> do - let defaultODT = getDataFileName "reference.odt" >>= B.readFile - case datadir of - Nothing -> defaultODT - Just d -> do - exists <- doesFileExist (d </> "reference.odt") - if exists - then B.readFile (d </> "reference.odt") - else defaultODT + Nothing -> (B.fromChunks . (:[])) `fmap` + readDataFile datadir "reference.odt" -- handle pictures picEntriesRef <- newIORef ([] :: [Entry]) let sourceDir = writerSourceDirectory opts doc' <- bottomUpM (transformPic sourceDir picEntriesRef) doc let newContents = writeOpenDocument opts{writerWrapText = False} doc' epochtime <- floor `fmap` getPOSIXTime - let contentEntry = toEntry "content.xml" epochtime $ fromString newContents + let contentEntry = toEntry "content.xml" epochtime $ fromStringLazy newContents picEntries <- readIORef picEntriesRef let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries -- construct META-INF/manifest.xml based on archive @@ -87,7 +78,7 @@ writeODT mbRefOdt opts doc = do ] let files = [ ent | ent <- filesInArchive archive, not ("META-INF" `isPrefixOf` ent) ] let manifestEntry = toEntry "META-INF/manifest.xml" epochtime - $ fromString $ show + $ fromStringLazy $ show $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ ( inTags True "manifest:manifest" @@ -100,21 +91,43 @@ writeODT mbRefOdt opts doc = do ) ) let archive' = addEntryToArchive manifestEntry archive - return $ fromArchive archive' + let metaEntry = toEntry "meta.xml" epochtime + $ fromStringLazy $ show + $ 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.2")] + $ ( inTagsSimple "office:meta" + $ ( inTagsSimple "dc:title" (text $ escapeStringForXML (stringify title)) + ) + ) + ) + let archive'' = addEntryToArchive metaEntry archive' + return $ fromArchive archive'' transformPic :: FilePath -> IORef [Entry] -> Inline -> IO Inline -transformPic sourceDir entriesRef (Image lab (src,tit)) = do - let src' = unEscapeString src - mbSize <- readImageSize src' - let tit' = case mbSize of - Just s -> let (w,h) = sizeInPoints s - in show w ++ "x" ++ show h - Nothing -> tit - entries <- readIORef entriesRef - let newsrc = "Pictures/" ++ show (length entries) ++ takeExtension src' - E.catch (readEntry [] (sourceDir </> src') >>= \entry -> - modifyIORef entriesRef (entry{ eRelativePath = newsrc } :) >> - return (Image lab (newsrc, tit'))) - (\(_::E.IOException) -> return (Emph lab)) +transformPic sourceDir entriesRef (Image lab (src,_)) = do + res <- liftIO $ E.try $ fetchItem sourceDir src + case res of + Left (_ :: E.SomeException) -> do + liftIO $ warn $ "Could not find image `" ++ src ++ "', skipping..." + return $ Emph lab + Right (img, _) -> do + let size = imageSize img + let (w,h) = maybe (0,0) id $ sizeInPoints `fmap` size + let tit' = show w ++ "x" ++ show h + entries <- readIORef entriesRef + let newsrc = "Pictures/" ++ show (length entries) ++ takeExtension src + let toLazy = B.fromChunks . (:[]) + epochtime <- floor `fmap` getPOSIXTime + let entry = toEntry newsrc epochtime $ toLazy img + modifyIORef entriesRef (entry:) + return $ Image lab (newsrc, tit') transformPic _ _ x = return x diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index a0317511a..b59e096c9 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to OpenDocument XML. -} module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Options import Text.Pandoc.XML import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Readers.TeXMath @@ -287,7 +287,7 @@ blockToOpenDocument :: WriterOptions -> Block -> State WriterState Doc blockToOpenDocument o bs | Plain b <- bs = inParagraphTags =<< inlinesToOpenDocument o b | Para b <- bs = inParagraphTags =<< inlinesToOpenDocument o b - | Header i b <- bs = setFirstPara >> + | Header i _ b <- bs = setFirstPara >> (inHeaderTags i =<< inlinesToOpenDocument o b) | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b | DefinitionList b <- bs = setFirstPara >> defList b diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 7eb943a22..4e7b21e35 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Org Copyright : Copyright (C) 2010 Puneeth Chaganti - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : Puneeth Chaganti <punchagan@gmail.com> Stability : alpha @@ -32,14 +32,15 @@ Org-Mode: <http://orgmode.org> -} module Text.Pandoc.Writers.Org ( writeOrg) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Options +import Text.Pandoc.Shared import Text.Pandoc.Pretty import Text.Pandoc.Templates (renderTemplate) import Data.List ( intersect, intersperse, transpose ) import Control.Monad.State import Control.Applicative ( (<$>) ) -data WriterState = +data WriterState = WriterState { stNotes :: [[Block]] , stLinks :: Bool , stImages :: Bool @@ -49,7 +50,7 @@ data WriterState = -- | Convert Pandoc to Org. writeOrg :: WriterOptions -> Pandoc -> String -writeOrg opts document = +writeOrg opts document = let st = WriterState { stNotes = [], stLinks = False, stImages = False, stHasMath = False, stOptions = opts } @@ -82,8 +83,8 @@ pandocToOrg (Pandoc (Meta tit auth dat) blocks) = do -- | Return Org representation of notes. notesToOrg :: [[Block]] -> State WriterState Doc -notesToOrg notes = - mapM (\(num, note) -> noteToOrg num note) (zip [1..] notes) >>= +notesToOrg notes = + mapM (\(num, note) -> noteToOrg num note) (zip [1..] notes) >>= return . vsep -- | Return Org representation of a note. @@ -106,45 +107,49 @@ titleToOrg :: [Inline] -> State WriterState Doc titleToOrg [] = return empty titleToOrg lst = do contents <- inlineListToOrg lst - return $ "#+TITLE: " <> contents + return $ "#+TITLE: " <> contents --- | Convert Pandoc block element to Org. +-- | Convert Pandoc block element to Org. blockToOrg :: Block -- ^ Block element - -> State WriterState Doc + -> State WriterState Doc blockToOrg Null = return empty blockToOrg (Plain inlines) = inlineListToOrg inlines -blockToOrg (Para [Image txt (src,tit)]) = do - capt <- inlineListToOrg txt +-- title beginning with fig: indicates that the image is a figure +blockToOrg (Para [Image txt (src,'f':'i':'g':':':tit)]) = do + capt <- if null txt + then return empty + else (\c -> "#+CAPTION: " <> c <> blankline) `fmap` + inlineListToOrg txt img <- inlineToOrg (Image txt (src,tit)) - return $ "#+CAPTION: " <> capt <> blankline <> img + return $ capt <> img blockToOrg (Para inlines) = do contents <- inlineListToOrg inlines return $ contents <> blankline -blockToOrg (RawBlock "html" str) = +blockToOrg (RawBlock "html" str) = return $ blankline $$ "#+BEGIN_HTML" $$ nest 2 (text str) $$ "#+END_HTML" $$ blankline blockToOrg (RawBlock f str) | f == "org" || f == "latex" || f == "tex" = return $ text str blockToOrg (RawBlock _ _) = return empty blockToOrg HorizontalRule = return $ blankline $$ "--------------" $$ blankline -blockToOrg (Header level inlines) = do +blockToOrg (Header level _ inlines) = do contents <- inlineListToOrg inlines let headerStr = text $ if level > 999 then " " else replicate level '*' return $ headerStr <> " " <> contents <> blankline blockToOrg (CodeBlock (_,classes,_) str) = do opts <- stOptions <$> get let tabstop = writerTabStop opts - let at = classes `intersect` ["asymptote", "C", "clojure", "css", "ditaa", - "dot", "emacs-lisp", "gnuplot", "haskell", "js", "latex", - "ledger", "lisp", "matlab", "mscgen", "ocaml", "octave", - "oz", "perl", "plantuml", "python", "R", "ruby", "sass", + let at = classes `intersect` ["asymptote", "C", "clojure", "css", "ditaa", + "dot", "emacs-lisp", "gnuplot", "haskell", "js", "latex", + "ledger", "lisp", "matlab", "mscgen", "ocaml", "octave", + "oz", "perl", "plantuml", "python", "R", "ruby", "sass", "scheme", "screen", "sh", "sql", "sqlite"] let (beg, end) = case at of [] -> ("#+BEGIN_EXAMPLE", "#+END_EXAMPLE") (x:_) -> ("#+BEGIN_SRC " ++ x, "#+END_SRC") return $ text beg $$ nest tabstop (text str) $$ text end $$ blankline blockToOrg (BlockQuote blocks) = do - contents <- blockListToOrg blocks + contents <- blockListToOrg blocks return $ blankline $$ "#+BEGIN_QUOTE" $$ nest 2 contents $$ "#+END_QUOTE" $$ blankline blockToOrg (Table caption' _ _ headers rows) = do @@ -155,11 +160,11 @@ blockToOrg (Table caption' _ _ headers rows) = do headers' <- mapM blockListToOrg headers rawRows <- mapM (mapM blockListToOrg) rows let numChars = maximum . map offset - -- FIXME: width is not being used. + -- FIXME: width is not being used. let widthsInChars = map ((+2) . numChars) $ transpose (headers' : rawRows) - -- FIXME: Org doesn't allow blocks with height more than 1. - let hpipeBlocks blocks = hcat [beg, middle, end] + -- FIXME: Org doesn't allow blocks with height more than 1. + let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (map height blocks) sep' = lblock 3 $ vcat (map text $ replicate h " | ") beg = lblock 2 $ vcat (map text $ replicate h "| ") @@ -170,7 +175,7 @@ blockToOrg (Table caption' _ _ headers rows) = do 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) $ + (hcat $ intersperse (char ch <> char '+' <> char ch) $ map (\l -> text $ replicate l ch) widthsInChars) <> char ch <> char '|' let body = vcat rows' @@ -186,7 +191,7 @@ blockToOrg (OrderedList (start, _, delim) items) = do let delim' = case delim of TwoParens -> OneParen x -> x - let markers = take (length items) $ orderedListMarkers + let markers = take (length items) $ orderedListMarkers (start, Decimal, delim') let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m @@ -222,7 +227,7 @@ definitionListItemToOrg (label, defs) = do -- | Convert list of Pandoc block elements to Org. blockListToOrg :: [Block] -- ^ List of block elements - -> State WriterState Doc + -> State WriterState Doc blockListToOrg blocks = mapM blockToOrg blocks >>= return . vcat -- | Convert list of Pandoc inline elements to Org. @@ -231,19 +236,19 @@ inlineListToOrg lst = mapM inlineToOrg lst >>= return . hcat -- | Convert Pandoc inline element to Org. inlineToOrg :: Inline -> State WriterState Doc -inlineToOrg (Emph lst) = do +inlineToOrg (Emph lst) = do contents <- inlineListToOrg lst return $ "/" <> contents <> "/" inlineToOrg (Strong lst) = do contents <- inlineListToOrg lst return $ "*" <> contents <> "*" -inlineToOrg (Strikeout lst) = do +inlineToOrg (Strikeout lst) = do contents <- inlineListToOrg lst return $ "+" <> contents <> "+" -inlineToOrg (Superscript lst) = do +inlineToOrg (Superscript lst) = do contents <- inlineListToOrg lst return $ "^{" <> contents <> "}" -inlineToOrg (Subscript lst) = do +inlineToOrg (Subscript lst) = do contents <- inlineListToOrg lst return $ "_{" <> contents <> "}" inlineToOrg (SmallCaps lst) = inlineListToOrg lst @@ -267,7 +272,7 @@ inlineToOrg (LineBreak) = return cr -- there's no line break in Org inlineToOrg Space = return space inlineToOrg (Link txt (src, _)) = do case txt of - [Code _ x] | x == src -> -- autolink + [Str x] | escapeURI x == src -> -- autolink do modify $ \s -> s{ stLinks = True } return $ "[[" <> text x <> "]]" _ -> do contents <- inlineListToOrg txt @@ -276,7 +281,7 @@ inlineToOrg (Link txt (src, _)) = do inlineToOrg (Image _ (source, _)) = do modify $ \s -> s{ stImages = True } return $ "[[" <> text source <> "]]" -inlineToOrg (Note contents) = do +inlineToOrg (Note contents) = do -- add to notes in state notes <- get >>= (return . stNotes) modify $ \st -> st { stNotes = contents:notes } diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index d98079940..89acd2ef5 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -18,9 +18,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Writers.RST + Module : Text.Pandoc.Writers.RST Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -32,7 +32,8 @@ reStructuredText: <http://docutils.sourceforge.net/rst.html> -} module Text.Pandoc.Writers.RST ( writeRST) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Options +import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Data.List ( isPrefixOf, intersperse, transpose ) import Text.Pandoc.Pretty @@ -42,17 +43,17 @@ import Data.Char (isSpace) type Refs = [([Inline], Target)] -data WriterState = +data WriterState = WriterState { stNotes :: [[Block]] , stLinks :: Refs - , stImages :: Refs + , stImages :: [([Inline], (String, String, Maybe String))] , stHasMath :: Bool , stOptions :: WriterOptions } -- | Convert Pandoc to RST. writeRST :: WriterOptions -> Pandoc -> String -writeRST opts document = +writeRST opts document = let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stOptions = opts } @@ -78,7 +79,9 @@ pandocToRST (Pandoc (Meta tit auth dat) blocks) = do let context = writerVariables opts ++ [ ("body", main) , ("title", render Nothing title) - , ("date", render colwidth date) ] ++ + , ("date", render colwidth date) + , ("toc", if writerTableOfContents opts then "yes" else "") + , ("toc-depth", show (writerTOCDepth opts)) ] ++ [ ("math", "yes") | hasMath ] ++ [ ("author", render colwidth a) | a <- authors ] if writerStandalone opts @@ -89,8 +92,8 @@ pandocToRST (Pandoc (Meta tit auth dat) blocks) = do refsToRST :: Refs -> State WriterState Doc refsToRST refs = mapM keyToRST refs >>= return . vcat --- | Return RST representation of a reference key. -keyToRST :: ([Inline], (String, String)) +-- | Return RST representation of a reference key. +keyToRST :: ([Inline], (String, String)) -> State WriterState Doc keyToRST (label, (src, _)) = do label' <- inlineListToRST label @@ -101,7 +104,7 @@ keyToRST (label, (src, _)) = do -- | Return RST representation of notes. notesToRST :: [[Block]] -> State WriterState Doc -notesToRST notes = +notesToRST notes = mapM (\(num, note) -> noteToRST num note) (zip [1..] notes) >>= return . vsep @@ -110,18 +113,23 @@ noteToRST :: Int -> [Block] -> State WriterState Doc noteToRST num note = do contents <- blockListToRST note let marker = ".. [" <> text (show num) <> "]" - return $ marker $$ nest 3 contents + return $ nowrap $ marker $$ nest 3 contents -- | Return RST representation of picture reference table. -pictRefsToRST :: Refs -> State WriterState Doc +pictRefsToRST :: [([Inline], (String, String, Maybe String))] + -> State WriterState Doc pictRefsToRST refs = mapM pictToRST refs >>= return . vcat --- | Return RST representation of a picture substitution reference. -pictToRST :: ([Inline], (String, String)) +-- | Return RST representation of a picture substitution reference. +pictToRST :: ([Inline], (String, String,Maybe String)) -> State WriterState Doc -pictToRST (label, (src, _)) = do +pictToRST (label, (src, _, mbtarget)) = do label' <- inlineListToRST label - return $ ".. |" <> label' <> "| image:: " <> text src + return $ nowrap + $ ".. |" <> label' <> "| image:: " <> text src + $$ case mbtarget of + Nothing -> empty + Just t -> " :target: " <> text t -- | Escape special characters for RST. escapeString :: String -> String @@ -135,26 +143,30 @@ titleToRST lst = do let border = text (replicate titleLength '=') return $ border $$ contents $$ border --- | Convert Pandoc block element to RST. +-- | Convert Pandoc block element to RST. blockToRST :: Block -- ^ Block element - -> State WriterState Doc + -> State WriterState Doc blockToRST Null = return empty blockToRST (Plain inlines) = inlineListToRST inlines -blockToRST (Para [Image txt (src,tit)]) = do +-- title beginning with fig: indicates that the image is a figure +blockToRST (Para [Image txt (src,'f':'i':'g':':':tit)]) = do capt <- inlineListToRST txt let fig = "figure:: " <> text src - let align = ":align: center" let alt = ":alt: " <> if null tit then capt else text tit - return $ hang 3 ".. " $ fig $$ align $$ alt $+$ capt $$ blankline -blockToRST (Para inlines) = do - contents <- inlineListToRST inlines - return $ contents <> blankline + return $ hang 3 ".. " $ fig $$ alt $+$ capt $$ blankline +blockToRST (Para inlines) + | LineBreak `elem` inlines = do -- use line block if LineBreaks + lns <- mapM inlineListToRST $ splitBy (==LineBreak) inlines + return $ (vcat $ map (text "| " <>) lns) <> blankline + | otherwise = do + contents <- inlineListToRST inlines + return $ contents <> blankline blockToRST (RawBlock f str) = return $ blankline <> ".. raw:: " <> text f $+$ (nest 3 $ text str) $$ blankline blockToRST HorizontalRule = return $ blankline $$ "--------------" $$ blankline -blockToRST (Header level inlines) = do +blockToRST (Header level _ inlines) = do contents <- inlineListToRST inlines let headerChar = if level > 5 then ' ' else "=-~^'" !! (level - 1) let border = text $ replicate (offset contents) headerChar @@ -163,12 +175,12 @@ blockToRST (CodeBlock (_,classes,_) str) = do opts <- stOptions <$> get let tabstop = writerTabStop opts if "haskell" `elem` classes && "literate" `elem` classes && - writerLiterateHaskell opts + isEnabled Ext_literate_haskell opts then return $ prefixed "> " (text str) $$ blankline else return $ "::" $+$ nest tabstop (text str) $$ blankline blockToRST (BlockQuote blocks) = do tabstop <- get >>= (return . writerTabStop . stOptions) - contents <- blockListToRST blocks + contents <- blockListToRST blocks return $ (nest tabstop contents) <> blankline blockToRST (Table caption _ widths headers rows) = do caption' <- inlineListToRST caption @@ -184,7 +196,7 @@ blockToRST (Table caption _ widths headers rows) = do if isSimple then map ((+2) . numChars) $ transpose (headers' : rawRows) else map (floor . (fromIntegral (writerColumns opts) *)) widths - let hpipeBlocks blocks = hcat [beg, middle, end] + let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (map height blocks) sep' = lblock 3 $ vcat (map text $ replicate h " | ") beg = lblock 2 $ vcat (map text $ replicate h "| ") @@ -195,7 +207,7 @@ blockToRST (Table caption _ widths headers rows) = do rows' <- mapM (\row -> do cols <- mapM blockListToRST row return $ makeRow cols) rows let border ch = char '+' <> char ch <> - (hcat $ intersperse (char ch <> char '+' <> char ch) $ + (hcat $ intersperse (char ch <> char '+' <> char ch) $ map (\l -> text $ replicate l ch) widthsInChars) <> char ch <> char '+' let body = vcat $ intersperse (border '-') rows' @@ -208,9 +220,9 @@ blockToRST (BulletList items) = do -- ensure that sublists have preceding blank line return $ blankline $$ vcat contents $$ blankline blockToRST (OrderedList (start, style', delim) items) = do - let markers = if start == 1 && style' == DefaultStyle && delim == DefaultDelim + let markers = if start == 1 && style' == DefaultStyle && delim == DefaultDelim then take (length items) $ repeat "#." - else take (length items) $ orderedListMarkers + else take (length items) $ orderedListMarkers (start, style', delim) let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m @@ -249,7 +261,7 @@ definitionListItemToRST (label, defs) = do -- | Convert list of Pandoc block elements to RST. blockListToRST :: [Block] -- ^ List of block elements - -> State WriterState Doc + -> State WriterState Doc blockListToRST blocks = mapM blockToRST blocks >>= return . vcat -- | Convert list of Pandoc inline elements to RST. @@ -303,19 +315,19 @@ inlineListToRST lst = mapM inlineToRST (insertBS lst) >>= return . hcat -- | Convert Pandoc inline element to RST. inlineToRST :: Inline -> State WriterState Doc -inlineToRST (Emph lst) = do +inlineToRST (Emph lst) = do contents <- inlineListToRST lst return $ "*" <> contents <> "*" inlineToRST (Strong lst) = do contents <- inlineListToRST lst return $ "**" <> contents <> "**" -inlineToRST (Strikeout lst) = do +inlineToRST (Strikeout lst) = do contents <- inlineListToRST lst return $ "[STRIKEOUT:" <> contents <> "]" -inlineToRST (Superscript lst) = do +inlineToRST (Superscript lst) = do contents <- inlineListToRST lst return $ ":sup:`" <> contents <> "`" -inlineToRST (Subscript lst) = do +inlineToRST (Subscript lst) = do contents <- inlineListToRST lst return $ ":sub:`" <> contents <> "`" inlineToRST (SmallCaps lst) = inlineListToRST lst @@ -339,39 +351,53 @@ inlineToRST (Math t str) = do else blankline $$ (".. math:: " <> text str) $$ blankline inlineToRST (RawInline "rst" x) = return $ text x inlineToRST (RawInline _ _) = return empty -inlineToRST (LineBreak) = return cr -- there's no line break in RST +inlineToRST (LineBreak) = return cr -- there's no line break in RST (see Para) inlineToRST Space = return space -inlineToRST (Link [Code _ str] (src, _)) | src == str || - src == "mailto:" ++ str = do +-- autolink +inlineToRST (Link [Str str] (src, _)) + | if "mailto:" `isPrefixOf` src + then src == escapeURI ("mailto:" ++ str) + else src == escapeURI str = do let srcSuffix = if isPrefixOf "mailto:" src then drop 7 src else src return $ text srcSuffix +inlineToRST (Link [Image alt (imgsrc,imgtit)] (src, _tit)) = do + label <- registerImage alt (imgsrc,imgtit) (Just src) + return $ "|" <> label <> "|" inlineToRST (Link txt (src, tit)) = do useReferenceLinks <- get >>= return . writerReferenceLinks . stOptions linktext <- inlineListToRST $ normalizeSpaces txt if useReferenceLinks then do refs <- get >>= return . stLinks - let refs' = if (txt, (src, tit)) `elem` refs - then refs - else (txt, (src, tit)):refs - modify $ \st -> st { stLinks = refs' } - return $ "`" <> linktext <> "`_" - else return $ "`" <> linktext <> " <" <> text src <> ">`_" + case lookup txt refs of + Just (src',tit') -> + if src == src' && tit == tit' + then return $ "`" <> linktext <> "`_" + else do -- duplicate label, use non-reference link + return $ "`" <> linktext <> " <" <> text src <> ">`__" + Nothing -> do + modify $ \st -> st { stLinks = (txt,(src,tit)):refs } + return $ "`" <> linktext <> "`_" + else return $ "`" <> linktext <> " <" <> text src <> ">`__" inlineToRST (Image alternate (source, tit)) = do - pics <- get >>= return . stImages - let labelsUsed = map fst pics - let txt = if null alternate || alternate == [Str ""] || - alternate `elem` labelsUsed - then [Str $ "image" ++ show (length pics)] - else alternate - let pics' = if (txt, (source, tit)) `elem` pics - then pics - else (txt, (source, tit)):pics - modify $ \st -> st { stImages = pics' } - label <- inlineListToRST txt + label <- registerImage alternate (source,tit) Nothing return $ "|" <> label <> "|" -inlineToRST (Note contents) = do +inlineToRST (Note contents) = do -- add to notes in state notes <- get >>= return . stNotes modify $ \st -> st { stNotes = contents:notes } let ref = show $ (length notes) + 1 return $ " [" <> text ref <> "]_" + +registerImage :: [Inline] -> Target -> Maybe String -> State WriterState Doc +registerImage alt (src,tit) mbtarget = do + pics <- get >>= return . stImages + txt <- case lookup alt pics of + Just (s,t,mbt) | (s,t,mbt) == (src,tit,mbtarget) -> return alt + _ -> do + let alt' = if null alt || alt == [Str ""] + then [Str $ "image" ++ show (length pics)] + else alt + modify $ \st -> st { stImages = + (alt', (src,tit, mbtarget)):stImages st } + return alt' + inlineListToRST txt diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 91df240af..ca33cb0e9 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -19,27 +19,28 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.RTF Copyright : Copyright (C) 2006-2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha + Stability : alpha Portability : portable Conversion of 'Pandoc' documents to RTF (rich text format). -} -{-# LANGUAGE ScopedTypeVariables #-} -module Text.Pandoc.Writers.RTF ( writeRTF, rtfEmbedImage ) where +module Text.Pandoc.Writers.RTF ( writeRTF, writeRTFWithEmbeddedImages ) where import Text.Pandoc.Definition +import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Readers.TeXMath import Text.Pandoc.Templates (renderTemplate) +import Text.Pandoc.Generic (bottomUpM) import Data.List ( isSuffixOf, intercalate ) import Data.Char ( ord, isDigit, toLower ) import System.FilePath ( takeExtension ) import qualified Data.ByteString as B import Text.Printf ( printf ) import Network.URI ( isAbsoluteURI, unEscapeString ) -import qualified Control.Exception as E (catch, IOException) +import qualified Control.Exception as E -- | Convert Image inlines into a raw RTF embedded image, read from a file. -- If file not found or filetype not jpeg or png, leave the inline unchanged. @@ -49,7 +50,8 @@ rtfEmbedImage x@(Image _ (src,_)) = do if ext `elem` [".jpg",".jpeg",".png"] && not (isAbsoluteURI src) then do let src' = unEscapeString src - imgdata <- E.catch (B.readFile src') (\(_::E.IOException) -> return B.empty) + imgdata <- E.catch (B.readFile src') + (\e -> let _ = (e :: E.SomeException) in return B.empty) let bytes = map (printf "%02x") $ B.unpack imgdata let filetype = case ext of ".jpg" -> "\\jpegblip" @@ -63,32 +65,40 @@ rtfEmbedImage x@(Image _ (src,_)) = do else return x rtfEmbedImage x = return x +-- | Convert Pandoc to a string in rich text format, with +-- images embedded as encoded binary data. +writeRTFWithEmbeddedImages :: WriterOptions -> Pandoc -> IO String +writeRTFWithEmbeddedImages options doc = + writeRTF options `fmap` bottomUpM rtfEmbedImage doc + -- | Convert Pandoc to a string in rich text format. writeRTF :: WriterOptions -> Pandoc -> String -writeRTF options (Pandoc (Meta title authors date) blocks) = +writeRTF options (Pandoc (Meta title authors date) blocks) = let titletext = inlineListToRTF title authorstext = map inlineListToRTF authors datetext = inlineListToRTF date spacer = not $ all null $ titletext : datetext : authorstext body = concatMap (blockToRTF 0 AlignDefault) blocks + isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options + isTOCHeader _ = False context = writerVariables options ++ [ ("body", body) , ("title", titletext) , ("date", datetext) ] ++ [ ("author", a) | a <- authorstext ] ++ [ ("spacer", "yes") | spacer ] ++ - [ ("toc", tableOfContents $ filter isHeaderBlock blocks) | + [ ("toc", tableOfContents $ filter isTOCHeader blocks) | writerTableOfContents options ] in if writerStandalone options then renderTemplate context $ writerTemplate options else body -- | Construct table of contents from list of header blocks. -tableOfContents :: [Block] -> String +tableOfContents :: [Block] -> String tableOfContents headers = let contentsTree = hierarchicalize headers - in concatMap (blockToRTF 0 AlignDefault) $ - [Header 1 [Str "Contents"], + in concatMap (blockToRTF 0 AlignDefault) $ + [Header 1 nullAttr [Str "Contents"], BulletList (map elementToListItem contentsTree)] elementToListItem :: Element -> [Block] @@ -102,7 +112,7 @@ elementToListItem (Sec _ _ _ sectext subsecs) = [Plain sectext] ++ handleUnicode :: String -> String handleUnicode [] = [] handleUnicode (c:cs) = - if ord c > 127 + if ord c > 127 then '\\':'u':(show (ord c)) ++ "?" ++ handleUnicode cs else c:(handleUnicode cs) @@ -132,32 +142,32 @@ rtfParSpaced :: Int -- ^ space after (in twips) -> Int -- ^ first line indent (relative to block) (in twips) -> Alignment -- ^ alignment -> String -- ^ string with content - -> String -rtfParSpaced spaceAfter indent firstLineIndent alignment content = + -> String +rtfParSpaced spaceAfter indent firstLineIndent alignment content = let alignString = case alignment of AlignLeft -> "\\ql " AlignRight -> "\\qr " AlignCenter -> "\\qc " AlignDefault -> "\\ql " in "{\\pard " ++ alignString ++ - "\\f0 \\sa" ++ (show spaceAfter) ++ " \\li" ++ (show indent) ++ + "\\f0 \\sa" ++ (show spaceAfter) ++ " \\li" ++ (show indent) ++ " \\fi" ++ (show firstLineIndent) ++ " " ++ content ++ "\\par}\n" --- | Default paragraph. +-- | Default paragraph. rtfPar :: Int -- ^ block indent (in twips) -> Int -- ^ first line indent (relative to block) (in twips) -> Alignment -- ^ alignment -> String -- ^ string with content - -> String -rtfPar = rtfParSpaced 180 + -> String +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 -> String -- ^ string with content - -> String -rtfCompact = rtfParSpaced 0 + -> String +rtfCompact = rtfParSpaced 0 -- number of twips to indent indentIncrement :: Int @@ -174,7 +184,7 @@ bulletMarker indent = case indent `mod` 720 of -- | Returns appropriate (list of) ordered list markers for indent level. orderedMarkers :: Int -> ListAttributes -> [String] -orderedMarkers indent (start, style, delim) = +orderedMarkers indent (start, style, delim) = if style == DefaultStyle && delim == DefaultDelim then case indent `mod` 720 of 0 -> orderedListMarkers (start, Decimal, Period) @@ -187,30 +197,30 @@ blockToRTF :: Int -- ^ indent level -> Block -- ^ block to convert -> String blockToRTF _ _ Null = "" -blockToRTF indent alignment (Plain lst) = +blockToRTF indent alignment (Plain lst) = rtfCompact indent 0 alignment $ inlineListToRTF lst -blockToRTF indent alignment (Para lst) = +blockToRTF indent alignment (Para lst) = rtfPar indent 0 alignment $ inlineListToRTF lst -blockToRTF indent alignment (BlockQuote lst) = - concatMap (blockToRTF (indent + indentIncrement) alignment) lst +blockToRTF indent alignment (BlockQuote lst) = + concatMap (blockToRTF (indent + indentIncrement) alignment) lst blockToRTF indent _ (CodeBlock _ str) = rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str)) blockToRTF _ _ (RawBlock "rtf" str) = str blockToRTF _ _ (RawBlock _ _) = "" -blockToRTF indent alignment (BulletList lst) = spaceAtEnd $ +blockToRTF indent alignment (BulletList lst) = spaceAtEnd $ concatMap (listItemToRTF alignment indent (bulletMarker indent)) lst -blockToRTF indent alignment (OrderedList attribs lst) = spaceAtEnd $ concat $ +blockToRTF indent alignment (OrderedList attribs lst) = spaceAtEnd $ concat $ zipWith (listItemToRTF alignment indent) (orderedMarkers indent attribs) lst -blockToRTF indent alignment (DefinitionList lst) = spaceAtEnd $ +blockToRTF indent alignment (DefinitionList lst) = spaceAtEnd $ concatMap (definitionListItemToRTF alignment indent) lst -blockToRTF indent _ HorizontalRule = +blockToRTF indent _ HorizontalRule = rtfPar indent 0 AlignCenter "\\emdash\\emdash\\emdash\\emdash\\emdash" -blockToRTF indent alignment (Header level lst) = rtfPar indent 0 alignment $ +blockToRTF indent alignment (Header level _ lst) = rtfPar indent 0 alignment $ "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ inlineListToRTF lst -blockToRTF indent alignment (Table caption aligns sizes headers rows) = +blockToRTF indent alignment (Table caption aligns sizes headers rows) = (if all null headers then "" - else tableRowToRTF True indent aligns sizes headers) ++ + else tableRowToRTF True indent aligns sizes headers) ++ concatMap (tableRowToRTF False indent aligns sizes) rows ++ rtfPar indent 0 alignment (inlineListToRTF caption) @@ -232,7 +242,7 @@ tableRowToRTF header indent aligns sizes' cols = end = "}\n\\intbl\\row}\n" in start ++ columns ++ end -tableItemToRTF :: Int -> Alignment -> [Block] -> String +tableItemToRTF :: Int -> Alignment -> [Block] -> String tableItemToRTF indent alignment item = let contents = concatMap (blockToRTF indent alignment) item in "{\\intbl " ++ contents ++ "\\cell}\n" @@ -240,7 +250,7 @@ tableItemToRTF indent alignment item = -- | Ensure that there's the same amount of space after compact -- lists as after regular lists. spaceAtEnd :: String -> String -spaceAtEnd str = +spaceAtEnd str = if isSuffixOf "\\par}\n" str then (take ((length str) - 6) str) ++ "\\sa180\\par}\n" else str @@ -251,10 +261,10 @@ listItemToRTF :: Alignment -- ^ alignment -> String -- ^ list start marker -> [Block] -- ^ list item (list of blocks) -> [Char] -listItemToRTF alignment indent marker [] = - rtfCompact (indent + listIncrement) (0 - listIncrement) alignment - (marker ++ "\\tx" ++ (show listIncrement) ++ "\\tab ") -listItemToRTF alignment indent marker list = +listItemToRTF alignment indent marker [] = + rtfCompact (indent + listIncrement) (0 - listIncrement) alignment + (marker ++ "\\tx" ++ (show listIncrement) ++ "\\tab ") +listItemToRTF alignment indent marker list = let (first:rest) = map (blockToRTF (indent + listIncrement) alignment) list listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++ "\\tx" ++ show listIncrement ++ "\\tab" @@ -277,7 +287,7 @@ definitionListItemToRTF alignment indent (label, defs) = let labelText = blockToRTF indent alignment (Plain label) itemsText = concatMap (blockToRTF (indent + listIncrement) alignment) $ concat defs - in labelText ++ itemsText + in labelText ++ itemsText -- | Convert list of inline items to RTF. inlineListToRTF :: [Inline] -- ^ list of inlines to convert @@ -293,9 +303,9 @@ inlineToRTF (Strikeout lst) = "{\\strike " ++ (inlineListToRTF lst) ++ "}" inlineToRTF (Superscript lst) = "{\\super " ++ (inlineListToRTF lst) ++ "}" inlineToRTF (Subscript lst) = "{\\sub " ++ (inlineListToRTF lst) ++ "}" inlineToRTF (SmallCaps lst) = "{\\scaps " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Quoted SingleQuote lst) = +inlineToRTF (Quoted SingleQuote lst) = "\\u8216'" ++ (inlineListToRTF lst) ++ "\\u8217'" -inlineToRTF (Quoted DoubleQuote lst) = +inlineToRTF (Quoted DoubleQuote lst) = "\\u8220\"" ++ (inlineListToRTF lst) ++ "\\u8221\"" inlineToRTF (Code _ str) = "{\\f1 " ++ (codeStringToRTF str) ++ "}" inlineToRTF (Str str) = stringToRTF str @@ -305,11 +315,11 @@ inlineToRTF (RawInline "rtf" str) = str inlineToRTF (RawInline _ _) = "" inlineToRTF (LineBreak) = "\\line " inlineToRTF Space = " " -inlineToRTF (Link text (src, _)) = - "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ +inlineToRTF (Link text (src, _)) = + "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ "\"}}{\\fldrslt{\\ul\n" ++ (inlineListToRTF text) ++ "\n}}}\n" -inlineToRTF (Image _ (source, _)) = - "{\\cf1 [image: " ++ source ++ "]\\cf0}" +inlineToRTF (Image _ (source, _)) = + "{\\cf1 [image: " ++ source ++ "]\\cf0}" inlineToRTF (Note contents) = - "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++ + "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++ (concatMap (blockToRTF 0 AlignDefault) contents) ++ "}" diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 6bb782899..502a91967 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -19,16 +19,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Texinfo Copyright : Copyright (C) 2008-2010 John MacFarlane and Peter Wang - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha + Stability : alpha Portability : portable Conversion of 'Pandoc' format into Texinfo. -} module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where import Text.Pandoc.Definition +import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Printf ( printf ) @@ -40,10 +41,12 @@ import Text.Pandoc.Pretty import Network.URI ( isAbsoluteURI, unEscapeString ) import System.FilePath -data WriterState = +data WriterState = WriterState { stStrikeout :: Bool -- document contains strikeout , stSuperscript :: Bool -- document contains superscript , stSubscript :: Bool -- document contains subscript + , stEscapeComma :: Bool -- in a context where we need @comma + , stIdentifiers :: [String] -- header ids used already } {- TODO: @@ -53,14 +56,15 @@ data WriterState = -- | Convert Pandoc to Texinfo. writeTexinfo :: WriterOptions -> Pandoc -> String -writeTexinfo options document = - evalState (pandocToTexinfo options $ wrapTop document) $ - WriterState { stStrikeout = False, stSuperscript = False, stSubscript = False } +writeTexinfo options document = + evalState (pandocToTexinfo options $ wrapTop document) $ + WriterState { stStrikeout = False, stSuperscript = False, + stEscapeComma = False, stSubscript = False, stIdentifiers = [] } -- | Add a "Top" node around the document, needed by Texinfo. wrapTop :: Pandoc -> Pandoc wrapTop (Pandoc (Meta title authors date) blocks) = - Pandoc (Meta title authors date) (Header 0 title : blocks) + Pandoc (Meta title authors date) (Header 0 nullAttr title : blocks) pandocToTexinfo :: WriterOptions -> Pandoc -> State WriterState String pandocToTexinfo options (Pandoc (Meta title authors date) blocks) = do @@ -94,7 +98,6 @@ stringToTexinfo = escapeStringUsing texinfoEscapes where texinfoEscapes = [ ('{', "@{") , ('}', "@}") , ('@', "@@") - , (',', "@comma{}") -- only needed in argument lists , ('\160', "@ ") , ('\x2014', "---") , ('\x2013', "--") @@ -102,6 +105,14 @@ stringToTexinfo = escapeStringUsing texinfoEscapes , ('\x2019', "'") ] +escapeCommas :: State WriterState Doc -> State WriterState Doc +escapeCommas parser = do + oldEscapeComma <- gets stEscapeComma + modify $ \st -> st{ stEscapeComma = True } + res <- parser + modify $ \st -> st{ stEscapeComma = oldEscapeComma } + return res + -- | Puts contents into Texinfo command. inCmd :: String -> Doc -> Doc inCmd cmd contents = char '@' <> text cmd <> braces contents @@ -115,11 +126,14 @@ blockToTexinfo Null = return empty blockToTexinfo (Plain lst) = inlineListToTexinfo lst -blockToTexinfo (Para [Image txt (src,tit)]) = do - capt <- inlineListToTexinfo txt +-- title beginning with fig: indicates that the image is a figure +blockToTexinfo (Para [Image txt (src,'f':'i':'g':':':tit)]) = do + capt <- if null txt + then return empty + else (\c -> text "@caption" <> braces c) `fmap` + inlineListToTexinfo txt img <- inlineToTexinfo (Image txt (src,tit)) - return $ text "@float" $$ img $$ (text "@caption{" <> capt <> char '}') $$ - text "@end float" + return $ text "@float" $$ img $$ capt $$ text "@end float" blockToTexinfo (Para lst) = inlineListToTexinfo lst -- this is handled differently from Plain in blockListToTexinfo @@ -131,7 +145,8 @@ blockToTexinfo (BlockQuote lst) = do text "@end quotation" blockToTexinfo (CodeBlock _ str) = do - return $ text "@verbatim" $$ + return $ blankline $$ + text "@verbatim" $$ flush (text str) $$ text "@end verbatim" <> blankline @@ -181,19 +196,23 @@ blockToTexinfo HorizontalRule = text (take 72 $ repeat '-') $$ text "@end ifnottex" -blockToTexinfo (Header 0 lst) = do +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 lst) = do +blockToTexinfo (Header level _ lst) = do node <- inlineListForNode lst txt <- inlineListToTexinfo lst + idsUsed <- gets stIdentifiers + let id' = uniqueIdent lst idsUsed + modify $ \st -> st{ stIdentifiers = id' : idsUsed } return $ if (level > 0) && (level <= 4) - then blankline <> text "@node " <> node <> cr <> - text (seccmd level) <> txt + then blankline <> text "@node " <> node $$ + text (seccmd level) <> txt $$ + text "@anchor" <> braces (text $ '#':id') else txt where seccmd 1 = "@chapter " @@ -217,7 +236,7 @@ blockToTexinfo (Table caption aligns widths heads rows) = do else return $ "@columnfractions " ++ concatMap (printf "%.2f ") widths let tableBody = text ("@multitable " ++ colDescriptors) $$ headers $$ - vcat rowsText $$ + vcat rowsText $$ text "@end multitable" return $ if isEmpty captionText then tableBody <> blankline @@ -241,7 +260,7 @@ tableAnyRowToTexinfo :: String -> [[Block]] -> State WriterState Doc tableAnyRowToTexinfo itemtype aligns cols = - zipWithM alignedBlock aligns cols >>= + zipWithM alignedBlock aligns cols >>= return . (text itemtype $$) . foldl (\row item -> row $$ (if isEmpty row then empty else text " @tab ") <> item) empty @@ -268,7 +287,7 @@ blockListToTexinfo [] = return empty blockListToTexinfo (x:xs) = do x' <- blockToTexinfo x case x of - Header level _ -> do + Header level _ _ -> do -- We need need to insert a menu for this node. let (before, after) = break isHeader xs before' <- blockListToTexinfo before @@ -293,14 +312,14 @@ blockListToTexinfo (x:xs) = do return $ x' $$ xs' isHeader :: Block -> Bool -isHeader (Header _ _) = True -isHeader _ = False +isHeader (Header _ _ _) = True +isHeader _ = False collectNodes :: Int -> [Block] -> [Block] collectNodes _ [] = [] collectNodes level (x:xs) = case x of - (Header hl _) -> + (Header hl _ _) -> if hl < level then [] else if hl == level @@ -311,7 +330,7 @@ collectNodes level (x:xs) = makeMenuLine :: Block -> State WriterState Doc -makeMenuLine (Header _ lst) = do +makeMenuLine (Header _ _ lst) = do txt <- inlineListForNode lst return $ text "* " <> txt <> text "::" makeMenuLine _ = error "makeMenuLine called with non-Header block" @@ -358,8 +377,8 @@ inlineToTexinfo :: Inline -- ^ Inline to convert inlineToTexinfo (Emph lst) = inlineListToTexinfo lst >>= return . inCmd "emph" -inlineToTexinfo (Strong lst) = - inlineListToTexinfo lst >>= return . inCmd "strong" +inlineToTexinfo (Strong lst) = + inlineListToTexinfo lst >>= return . inCmd "strong" inlineToTexinfo (Strikeout lst) = do modify $ \st -> st{ stStrikeout = True } @@ -401,17 +420,21 @@ inlineToTexinfo (RawInline _ _) = return empty inlineToTexinfo (LineBreak) = return $ text "@*" inlineToTexinfo Space = return $ char ' ' +inlineToTexinfo (Link txt (src@('#':_), _)) = do + contents <- escapeCommas $ inlineListToTexinfo txt + return $ text "@ref" <> + braces (text (stringToTexinfo src) <> text "," <> contents) inlineToTexinfo (Link txt (src, _)) = do case txt of - [Code _ x] | x == src -> -- autolink + [Str x] | escapeURI x == src -> -- autolink do return $ text $ "@url{" ++ x ++ "}" - _ -> do contents <- inlineListToTexinfo txt + _ -> do contents <- escapeCommas $ inlineListToTexinfo txt let src1 = stringToTexinfo src return $ text ("@uref{" ++ src1 ++ ",") <> contents <> char '}' inlineToTexinfo (Image alternate (source, _)) = do - content <- inlineListToTexinfo alternate + content <- escapeCommas $ inlineListToTexinfo alternate return $ text ("@image{" ++ base ++ ",,,") <> content <> text "," <> text (ext ++ "}") where diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 26d5ec6d7..0a071c1f8 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Textile Copyright : Copyright (C) 2010 John MacFarlane - License : GNU GPL, version 2 or above + License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha @@ -31,7 +31,8 @@ Textile: <http://thresholdstate.com/articles/4312/the-textile-reference-manual> -} module Text.Pandoc.Writers.Textile ( writeTextile ) where import Text.Pandoc.Definition -import Text.Pandoc.Shared +import Text.Pandoc.Options +import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.XML ( escapeStringForXML ) import Data.List ( intercalate ) @@ -46,9 +47,9 @@ data WriterState = WriterState { -- | Convert Pandoc to Textile. writeTextile :: WriterOptions -> Pandoc -> String -writeTextile opts document = - evalState (pandocToTextile opts document) - (WriterState { stNotes = [], stListLevel = [], stUseTags = False }) +writeTextile opts document = + evalState (pandocToTextile opts document) + (WriterState { stNotes = [], stListLevel = [], stUseTags = False }) -- | Return Textile representation of document. pandocToTextile :: WriterOptions -> Pandoc -> State WriterState String @@ -90,17 +91,18 @@ escapeCharForTextile x = case x of escapeStringForTextile :: String -> String escapeStringForTextile = concatMap escapeCharForTextile --- | Convert Pandoc block element to Textile. +-- | Convert Pandoc block element to Textile. blockToTextile :: WriterOptions -- ^ Options -> Block -- ^ Block element - -> State WriterState String + -> State WriterState String blockToTextile _ Null = return "" -blockToTextile opts (Plain inlines) = +blockToTextile opts (Plain inlines) = inlineListToTextile opts inlines -blockToTextile opts (Para [Image txt (src,tit)]) = do +-- title beginning with fig: indicates that the image is a figure +blockToTextile opts (Para [Image txt (src,'f':'i':'g':':':tit)]) = do capt <- blockToTextile opts (Para txt) im <- inlineToTextile opts (Image txt (src,tit)) return $ im ++ "\n" ++ capt @@ -120,9 +122,10 @@ blockToTextile _ (RawBlock f str) = blockToTextile _ HorizontalRule = return "<hr />\n" -blockToTextile opts (Header level inlines) = do +blockToTextile opts (Header level (ident,_,_) inlines) = do contents <- inlineListToTextile opts inlines - let prefix = 'h' : (show level ++ ". ") + let attribs = if null ident then "" else "(#" ++ ident ++ ")" + let prefix = 'h' : show level ++ attribs ++ ". " return $ prefix ++ contents ++ "\n" blockToTextile _ (CodeBlock (_,classes,_) str) | any (all isSpace) (lines str) = @@ -236,7 +239,7 @@ listItemToTextile opts items = do -- | Convert definition list item (label, list of blocks) to Textile. definitionListItemToTextile :: WriterOptions - -> ([Inline],[[Block]]) + -> ([Inline],[[Block]]) -> State WriterState String definitionListItemToTextile opts (label, items) = do labelText <- inlineListToTextile opts label @@ -294,8 +297,8 @@ tableRowToTextile opts alignStrings rownum cols' = do 0 -> "header" x | x `rem` 2 == 1 -> "odd" _ -> "even" - cols'' <- sequence $ zipWith - (\alignment item -> tableItemToTextile opts celltype alignment item) + cols'' <- sequence $ zipWith + (\alignment item -> tableItemToTextile opts celltype alignment item) alignStrings cols' return $ "<tr class=\"" ++ rowclass ++ "\">\n" ++ unlines cols'' ++ "</tr>" @@ -320,7 +323,7 @@ tableItemToTextile opts celltype align' item = do -- | Convert list of Pandoc block elements to Textile. blockListToTextile :: WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> State WriterState String + -> State WriterState String blockListToTextile opts blocks = mapM (blockToTextile opts) blocks >>= return . vcat @@ -332,11 +335,11 @@ inlineListToTextile opts lst = -- | Convert Pandoc inline element to Textile. inlineToTextile :: WriterOptions -> Inline -> State WriterState String -inlineToTextile opts (Emph lst) = do +inlineToTextile opts (Emph lst) = do contents <- inlineListToTextile opts lst return $ if '_' `elem` contents then "<em>" ++ contents ++ "</em>" - else "_" ++ contents ++ "_" + else "_" ++ contents ++ "_" inlineToTextile opts (Strong lst) = do contents <- inlineListToTextile opts lst @@ -377,7 +380,7 @@ inlineToTextile opts (Cite _ lst) = inlineListToTextile opts lst inlineToTextile _ (Code _ str) = return $ if '@' `elem` str then "<tt>" ++ escapeStringForXML str ++ "</tt>" - else "@" ++ str ++ "@" + else "@" ++ str ++ "@" inlineToTextile _ (Str str) = return $ escapeStringForTextile str @@ -395,7 +398,10 @@ inlineToTextile _ Space = return " " inlineToTextile opts (Link txt (src, _)) = do label <- case txt of - [Code _ s] -> return s + [Code _ s] + | s == src -> return "$" + [Str s] + | s == src -> return "$" _ -> inlineListToTextile opts txt return $ "\"" ++ label ++ "\":" ++ src diff --git a/templates/epub-coverimage.html b/templates/epub-coverimage.html deleted file mode 100644 index cd778a145..000000000 --- a/templates/epub-coverimage.html +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title>$title$</title> -<style type="text/css">img{ max-width: 100%; }</style> -<link href="stylesheet.css" type="text/css" rel="stylesheet" /> -</head> -<body> -<div id="cover-image"> -<img src="$coverimage$" alt="$title$" /> -</div> -</body> -</html> diff --git a/templates/epub-page.html b/templates/epub-page.html deleted file mode 100644 index aa56170a0..000000000 --- a/templates/epub-page.html +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title>$title$</title> -<link href="stylesheet.css" type="text/css" rel="stylesheet" /> -</head> -<body> -<h1>$title$</h1> -$if(toc)$ -<div id="$idprefix$TOC"> -$toc$ -</div> -$endif$ -$body$ -</body> -</html> - diff --git a/templates/epub-titlepage.html b/templates/epub-titlepage.html deleted file mode 100644 index 790431bb7..000000000 --- a/templates/epub-titlepage.html +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> -<title>$title$</title> -<link href="stylesheet.css" type="text/css" rel="stylesheet" /> -</head> -<body> -<h1 class="title">$title$</h1> -$for(author)$ -<h2 class="author">$author$</h2> -$endfor$ -$if(date)$ -<h3 class="date">$date$</h3> -$endif$ -$if(toc)$ -<div id="$idprefix$TOC"> -$toc$ -</div> -$endif$ -</body> -</html> diff --git a/tests/Tests/Arbitrary.hs b/tests/Tests/Arbitrary.hs index 9d65e1f1f..d0000dcee 100644 --- a/tests/Tests/Arbitrary.hs +++ b/tests/Tests/Arbitrary.hs @@ -81,7 +81,7 @@ arbBlock n = frequency $ [ (10, liftM Plain $ arbInlines (n-1)) ]) , (5, do x1 <- choose (1 :: Int, 6) x2 <- arbInlines (n-1) - return (Header x1 x2)) + return (Header x1 nullAttr x2)) , (2, return HorizontalRule) ] ++ [x | x <- nesters, n > 0] where nesters = [ (5, liftM BlockQuote $ listOf1 $ arbBlock (n-1)) diff --git a/tests/Tests/Helpers.hs b/tests/Tests/Helpers.hs index 66879efed..d6cad345c 100644 --- a/tests/Tests/Helpers.hs +++ b/tests/Tests/Helpers.hs @@ -17,19 +17,20 @@ import Test.Framework import Test.Framework.Providers.HUnit import Test.Framework.Providers.QuickCheck2 import Test.HUnit (assertBool) -import Text.Pandoc.Shared (normalize, defaultWriterOptions, - WriterOptions(..), removeTrailingSpace) +import Text.Pandoc.Shared (normalize, trimr) +import Text.Pandoc.Options import Text.Pandoc.Writers.Native (writeNative) import Language.Haskell.TH.Quote (QuasiQuoter(..)) import Language.Haskell.TH.Syntax (Q, runIO) import qualified Test.QuickCheck.Property as QP -import System.Console.ANSI import Data.Algorithm.Diff lit :: QuasiQuoter lit = QuasiQuoter { quoteExp = (\a -> let b = rnl a in [|b|]) . filter (/= '\r') - , quotePat = error "Cannot use lit as a pattern" + , quotePat = error "Unimplemented" + , quoteType = error "Unimplemented" + , quoteDec = error "Unimplemented" } where rnl ('\n':xs) = xs rnl xs = xs @@ -40,7 +41,8 @@ file = quoteFile lit -- adapted from TH 2.5 code quoteFile :: QuasiQuoter -> QuasiQuoter quoteFile (QuasiQuoter { quoteExp = qe, quotePat = qp }) = - QuasiQuoter { quoteExp = get qe, quotePat = get qp } + QuasiQuoter { quoteExp = get qe, quotePat = get qp, + quoteType = error "Unimplemented", quoteDec = error "Unimplemented" } where get :: (String -> Q a) -> String -> Q a get old_quoter file_name = do { file_cts <- runIO (readFile file_name) @@ -54,25 +56,21 @@ test :: (ToString a, ToString b, ToString c) test fn name (input, expected) = testCase name $ assertBool msg (actual' == expected') where msg = nl ++ dashes "input" ++ nl ++ input' ++ nl ++ - dashes "expected" ++ nl ++ expected'' ++ - dashes "got" ++ nl ++ actual'' ++ + dashes "result" ++ nl ++ + unlines (map vividize diff) ++ dashes "" nl = "\n" input' = toString input - actual' = toString $ fn input - expected' = toString expected - diff = getDiff (lines expected') (lines actual') - expected'' = unlines $ map vividize $ filter (\(d,_) -> d /= S) diff - actual'' = unlines $ map vividize $ filter (\(d,_) -> d /= F) diff + actual' = lines $ toString $ fn input + expected' = lines $ toString expected + diff = getDiff expected' actual' dashes "" = replicate 72 '-' dashes x = replicate (72 - length x - 5) '-' ++ " " ++ x ++ " ---" -vividize :: (DI,String) -> String -vividize (B,s) = s -vividize (F,s) = s -vividize (S,s) = setSGRCode [SetColor Background Dull Red - , SetColor Foreground Vivid White] ++ s - ++ setSGRCode [Reset] +vividize :: Diff String -> String +vividize (Both s _) = " " ++ s +vividize (First s) = "- " ++ s +vividize (Second s) = "+ " ++ s property :: QP.Testable a => TestName -> a -> Test property = testProperty @@ -85,18 +83,16 @@ class ToString a where toString :: a -> String instance ToString Pandoc where - toString d = writeNative defaultWriterOptions{ writerStandalone = s } - $ toPandoc d + toString d = writeNative def{ writerStandalone = s } $ toPandoc d where s = case d of (Pandoc (Meta [] [] []) _) -> False _ -> True instance ToString Blocks where - toString = writeNative defaultWriterOptions . toPandoc + toString = writeNative def . toPandoc instance ToString Inlines where - toString = removeTrailingSpace . writeNative defaultWriterOptions . - toPandoc + toString = trimr . writeNative def . toPandoc instance ToString String where toString = id diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index 67eb51573..c5d677bac 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -10,23 +10,24 @@ import System.FilePath ( (</>), (<.>) ) import System.Directory import System.Exit import Data.Algorithm.Diff -import Text.Pandoc.Shared ( normalize, defaultWriterOptions ) +import Text.Pandoc.Shared ( normalize ) +import Text.Pandoc.Options import Text.Pandoc.Writers.Native ( writeNative ) import Text.Pandoc.Readers.Native ( readNative ) import Prelude hiding ( readFile ) import qualified Data.ByteString.Lazy as B -import Data.ByteString.Lazy.UTF8 (toString) +import Text.Pandoc.UTF8 (toStringLazy) import Text.Printf readFileUTF8 :: FilePath -> IO String -readFileUTF8 f = B.readFile f >>= return . toString +readFileUTF8 f = B.readFile f >>= return . toStringLazy pandocPath :: FilePath pandocPath = ".." </> "dist" </> "build" </> "pandoc" </> "pandoc" data TestResult = TestPassed | TestError ExitCode - | TestFailed String FilePath [(DI, String)] + | TestFailed String FilePath [Diff String] deriving (Eq) instance Show TestResult where @@ -38,13 +39,13 @@ instance Show TestResult where dash where dash = replicate 72 '-' -showDiff :: (Int,Int) -> [(DI, String)] -> String +showDiff :: (Int,Int) -> [Diff String] -> String showDiff _ [] = "" -showDiff (l,r) ((F, ln) : ds) = +showDiff (l,r) (First ln : ds) = printf "+%4d " l ++ ln ++ "\n" ++ showDiff (l+1,r) ds -showDiff (l,r) ((S, ln) : ds) = +showDiff (l,r) (Second ln : ds) = printf "-%4d " r ++ ln ++ "\n" ++ showDiff (l,r+1) ds -showDiff (l,r) ((B, _ ) : ds) = +showDiff (l,r) (Both _ _ : ds) = showDiff (l+1,r+1) ds tests :: [Test] @@ -56,6 +57,8 @@ tests = [ testGroup "markdown" "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" @@ -107,9 +110,23 @@ tests = [ testGroup "markdown" , 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 "tables" [] "tables.native" "tables.fb2" + , fb2WriterTest "math" [] "fb2.math.markdown" "fb2.math.fb2" + , fb2WriterTest "testsuite" [] "testsuite.native" "writer.fb2" + ] + , testGroup "mediawiki" + [ testGroup "writer" $ writerTests "mediawiki" + , test "reader" ["-r", "mediawiki", "-w", "native", "-s"] + "mediawiki-reader.wiki" "mediawiki-reader.native" + ] , testGroup "other writers" $ map (\f -> testGroup f $ writerTests f) [ "opendocument" , "context" , "texinfo" - , "man" , "plain" , "mediawiki", "rtf", "org", "asciidoc" + , "man" , "plain" , "rtf", "org", "asciidoc" ] ] @@ -130,8 +147,11 @@ lhsWriterTests format lhsReaderTest :: String -> Test lhsReaderTest format = testWithNormalize normalizer "lhs" ["-r", format, "-w", "native"] - ("lhs-test" <.> format) "lhs-test.native" - where normalizer = writeNative defaultWriterOptions . normalize . readNative + ("lhs-test" <.> format) norm + where normalizer = writeNative def . normalize . readNative + norm = if format == "markdown+lhs" + then "lhs-test-markdown.native" + else "lhs-test.native" writerTests :: String -> [Test] writerTests format @@ -142,14 +162,27 @@ writerTests format opts = ["-r", "native", "-w", format, "--columns=78"] s5WriterTest :: String -> [String] -> String -> Test -s5WriterTest modifier opts format +s5WriterTest modifier opts format = test (format ++ " writer (" ++ modifier ++ ")") - (["-r", "native", "-w", format] ++ opts) + (["-r", "native", "-w", format] ++ opts) "s5.native" ("s5." ++ modifier <.> "html") +fb2WriterTest :: String -> [String] -> String -> String -> Test +fb2WriterTest title opts inputfile normfile = + testWithNormalize (ignoreBinary . formatXML) + 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 + markdownCitationTests :: [Test] markdownCitationTests - = map styleToTest ["chicago-author-date","ieee","mhra"] + = map styleToTest ["chicago-author-date","ieee","mhra"] ++ [test "natbib" wopts "markdown-citations.txt" "markdown-citations.txt"] where @@ -179,10 +212,10 @@ testWithNormalize normalizer testname opts inp norm = testCase testname $ do (outputPath, hOut) <- openTempFile "" "pandoc-test" let inpPath = inp let normPath = norm - let options = ["--data-dir", ".."] ++ [inpPath] ++ opts + let options = ["--data-dir", ".." </> "data"] ++ [inpPath] ++ opts let cmd = pandocPath ++ " " ++ unwords options ph <- runProcess pandocPath options Nothing - (Just [("LANG","en_US.UTF-8"),("HOME", "./")]) Nothing (Just hOut) + (Just [("TMP","."),("LANG","en_US.UTF-8"),("HOME", "./")]) Nothing (Just hOut) (Just stderr) ec <- waitForProcess ph result <- if ec == ExitSuccess diff --git a/tests/Tests/Readers/LaTeX.hs b/tests/Tests/Readers/LaTeX.hs index d60026b20..271b32689 100644 --- a/tests/Tests/Readers/LaTeX.hs +++ b/tests/Tests/Readers/LaTeX.hs @@ -9,7 +9,7 @@ import Text.Pandoc.Builder import Text.Pandoc latex :: String -> Pandoc -latex = readLaTeX defaultParserState +latex = readLaTeX def infix 4 =: (=:) :: ToString c @@ -67,7 +67,8 @@ baseCitation = Citation{ citationId = "item1" , citationSuffix = [] , citationMode = AuthorInText , citationNoteNum = 0 - , citationHash = 0 } + , citationHash = 0 + } rt :: String -> Inlines rt = rawInline "latex" diff --git a/tests/Tests/Readers/Markdown.hs b/tests/Tests/Readers/Markdown.hs index 5ad974adf..6498c6f07 100644 --- a/tests/Tests/Readers/Markdown.hs +++ b/tests/Tests/Readers/Markdown.hs @@ -6,27 +6,117 @@ import Test.Framework import Tests.Helpers import Tests.Arbitrary() import Text.Pandoc.Builder +import qualified Data.Set as Set -- import Text.Pandoc.Shared ( normalize ) import Text.Pandoc markdown :: String -> Pandoc -markdown = readMarkdown defaultParserState{ stateStandalone = True } +markdown = readMarkdown def markdownSmart :: String -> Pandoc -markdownSmart = readMarkdown defaultParserState{ stateSmart = True } +markdownSmart = readMarkdown def { readerSmart = True } infix 4 =: (=:) :: ToString c => String -> (String, c) -> Test (=:) = test markdown +testBareLink :: (String, Inlines) -> Test +testBareLink (inp, ils) = + test (readMarkdown def{ readerExtensions = + Set.fromList [Ext_autolink_bare_uris] }) + inp (inp, doc $ para ils) + +autolink :: String -> Inlines +autolink s = link s "" (str s) + +bareLinkTests :: [(String, Inlines)] +bareLinkTests = + [ ("http://google.com is a search engine.", + autolink "http://google.com" <> " is a search engine.") + , ("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]", + autolink "http://en.wikipedia.org/wiki/Sprite_[computer_graphics]") + , ("http://en.wikipedia.org/wiki/Sprite_{computer_graphics}", + autolink "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/") + ] + {- p_markdown_round_trip :: Block -> Bool p_markdown_round_trip b = matches d' d'' where d' = normalize $ Pandoc (Meta [] [] []) [b] d'' = normalize - $ readMarkdown defaultParserState{ stateSmart = True } - $ writeMarkdown defaultWriterOptions d' + $ 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' @@ -43,6 +133,12 @@ tests = [ testGroup "inline code" "`*` {.haskell .special x=\"7\"}" =?> para (codeWith ("",["haskell","special"],[("x","7")]) "*") ] + , testGroup "raw LaTeX" + [ "in URL" =: + "\\begin\n" =?> para (text "\\begin") + ] + , "unbalanced brackets" =: + "[[[[[[[[[[[[[[[hi" =?> para (text "[[[[[[[[[[[[[[[hi") , testGroup "backslash escapes" [ "in URL" =: "[hi](/there\\))" @@ -57,6 +153,8 @@ tests = [ testGroup "inline code" "[hi]\n\n[hi]: /there\\.0" =?> para (link "/there.0" "" "hi") ] + , testGroup "bare URIs" + (map testBareLink bareLinkTests) , testGroup "smart punctuation" [ test markdownSmart "quote before ellipses" ("'...hi'" @@ -91,7 +189,8 @@ tests = [ testGroup "inline code" =?> para (note (para "See [^1]")) ] , testGroup "lhs" - [ test (readMarkdown defaultParserState{stateLiterateHaskell = True}) + [ test (readMarkdown def{ readerExtensions = Set.insert + Ext_literate_haskell $ readerExtensions def }) "inverse bird tracks and html" $ "> a\n\n< b\n\n<div>\n" =?> codeBlockWith ("",["sourceCode","literate","haskell"],[]) "a" diff --git a/tests/Tests/Readers/RST.hs b/tests/Tests/Readers/RST.hs index 3269092a6..0ad21f224 100644 --- a/tests/Tests/Readers/RST.hs +++ b/tests/Tests/Readers/RST.hs @@ -9,7 +9,7 @@ import Text.Pandoc.Builder import Text.Pandoc rst :: String -> Pandoc -rst = readRST defaultParserState{ stateStandalone = True } +rst = readRST def infix 4 =: (=:) :: ToString c @@ -18,8 +18,8 @@ infix 4 =: tests :: [Test] tests = [ "line block with blank line" =: - "| a\n|\n| b" =?> para (str "a" <> linebreak <> - linebreak <> str " " <> str "b") + "| a\n|\n| b" =?> para (str "a") <> + para (str "\160b") , "field list" =: [_LIT| :Hostname: media08 diff --git a/tests/Tests/Writers/ConTeXt.hs b/tests/Tests/Writers/ConTeXt.hs index beb6411f0..1beed33bb 100644 --- a/tests/Tests/Writers/ConTeXt.hs +++ b/tests/Tests/Writers/ConTeXt.hs @@ -8,11 +8,10 @@ import Tests.Helpers import Tests.Arbitrary() context :: (ToString a, ToPandoc a) => a -> String -context = writeConTeXt defaultWriterOptions . toPandoc +context = writeConTeXt def . toPandoc context' :: (ToString a, ToPandoc a) => a -> String -context' = writeConTeXt defaultWriterOptions{ writerWrapText = False } - . toPandoc +context' = writeConTeXt def{ writerWrapText = False } . toPandoc {- "my test" =: X =?> Y @@ -43,23 +42,24 @@ tests = [ testGroup "inline code" ] , testGroup "headers" [ "level 1" =: - header 1 "My header" =?> "\\section[my-header]{My header}" + headerWith ("my-header",[],[]) 1 "My header" =?> "\\section[my-header]{My header}" ] , testGroup "bullet lists" [ "nested" =: - bulletList [plain (text "top") - ,bulletList [plain (text "next") - ,bulletList [plain (text "bot")]]] - =?> [_LIT| -\startitemize + bulletList [ + plain (text "top") + <> bulletList [ + plain (text "next") + <> bulletList [plain (text "bot")] + ] + ] =?> [_LIT| +\startitemize[packed] \item top -\item - \startitemize + \startitemize[packed] \item next - \item - \startitemize + \startitemize[packed] \item bot \stopitemize diff --git a/tests/Tests/Writers/HTML.hs b/tests/Tests/Writers/HTML.hs index 8561aa421..5d6e301c5 100644 --- a/tests/Tests/Writers/HTML.hs +++ b/tests/Tests/Writers/HTML.hs @@ -9,7 +9,7 @@ import Tests.Arbitrary() import Text.Pandoc.Highlighting (languages) -- null if no hl support html :: (ToString a, ToPandoc a) => a -> String -html = writeHtmlString defaultWriterOptions{ writerWrapText = False } . toPandoc +html = writeHtmlString def{ writerWrapText = False } . toPandoc {- "my test" =: X =?> Y diff --git a/tests/Tests/Writers/LaTeX.hs b/tests/Tests/Writers/LaTeX.hs index 7987716f3..33d6ecc78 100644 --- a/tests/Tests/Writers/LaTeX.hs +++ b/tests/Tests/Writers/LaTeX.hs @@ -8,7 +8,7 @@ import Tests.Helpers import Tests.Arbitrary() latex :: (ToString a, ToPandoc a) => a -> String -latex = writeLaTeX defaultWriterOptions . toPandoc +latex = writeLaTeX def . toPandoc {- "my test" =: X =?> Y @@ -32,4 +32,8 @@ tests = [ testGroup "code blocks" [ "in footnotes" =: note (para "hi" <> codeBlock "hi") =?> "\\footnote{hi\n\n\\begin{Verbatim}\nhi\n\\end{Verbatim}\n}" ] + , testGroup "math" + [ "escape |" =: para (math "\\sigma|_{\\{x\\}}") =?> + "$\\sigma\\vert _{\\{x\\}}$" + ] ] diff --git a/tests/Tests/Writers/Markdown.hs b/tests/Tests/Writers/Markdown.hs index d90dc83b1..22ce8b27c 100644 --- a/tests/Tests/Writers/Markdown.hs +++ b/tests/Tests/Writers/Markdown.hs @@ -8,7 +8,7 @@ import Tests.Helpers import Tests.Arbitrary() markdown :: (ToString a, ToPandoc a) => a -> String -markdown = writeMarkdown defaultWriterOptions . toPandoc +markdown = writeMarkdown def . toPandoc {- "my test" =: X =?> Y diff --git a/tests/Tests/Writers/Native.hs b/tests/Tests/Writers/Native.hs index 19740e0f4..e199cf94e 100644 --- a/tests/Tests/Writers/Native.hs +++ b/tests/Tests/Writers/Native.hs @@ -8,11 +8,11 @@ import Tests.Arbitrary() p_write_rt :: Pandoc -> Bool p_write_rt d = - read (writeNative defaultWriterOptions{ writerStandalone = True } d) == d + read (writeNative def{ writerStandalone = True } d) == d p_write_blocks_rt :: [Block] -> Bool p_write_blocks_rt bs = length bs > 20 || - read (writeNative defaultWriterOptions (Pandoc (Meta [] [] []) bs)) == + read (writeNative def (Pandoc (Meta [] [] []) bs)) == bs tests :: [Test] diff --git a/tests/docbook-reader.docbook b/tests/docbook-reader.docbook index eb70dd33c..6173fa50e 100644 --- a/tests/docbook-reader.docbook +++ b/tests/docbook-reader.docbook @@ -915,7 +915,7 @@ These should not be escaped: \$ \\ \> \[ \{ <title>Autolinks</title> <para> With an ampersand: - <ulink url="http://example.com/?foo=1&bar=2"><literal>http://example.com/?foo=1&bar=2</literal></ulink> + <ulink url="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</ulink> </para> <itemizedlist> <listitem> @@ -925,7 +925,7 @@ These should not be escaped: \$ \\ \> \[ \{ </listitem> <listitem> <para> - <ulink url="http://example.com/"><literal>http://example.com/</literal></ulink> + <ulink url="http://example.com/">http://example.com/</ulink> </para> </listitem> <listitem> @@ -940,7 +940,7 @@ These should not be escaped: \$ \\ \> \[ \{ <blockquote> <para> Blockquoted: - <ulink url="http://example.com/"><literal>http://example.com/</literal></ulink> + <ulink url="http://example.com/">http://example.com/</ulink> </para> </blockquote> <para> diff --git a/tests/docbook-reader.native b/tests/docbook-reader.native index 3d403648b..9fe02f463 100644 --- a/tests/docbook-reader.native +++ b/tests/docbook-reader.native @@ -1,22 +1,22 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [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",Space,Str "markdown",Space,Str "test",Space,Str "suite."] -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] -,Header 3 [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 1 ("",[],[]) [Str "Headers"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] +,Header 3 ("",[],[]) [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"] ,Para [Str "Hi."] -,Header 1 [Str "Level",Space,Str "1"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 3 [Str "Level",Space,Str "3"] +,Header 1 ("",[],[]) [Str "Level",Space,Str "1"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] +,Header 3 ("",[],[]) [Str "Level",Space,Str "3"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] -,Header 2 [Str "Level",Space,Str "2"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] -,Header 1 [Str "Paragraphs"] +,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.",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\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet.",Space,Str "*",Space,Str "criminey."] -,Header 1 [Str "Block",Space,Str "Quotes"] +,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."]] @@ -34,13 +34,13 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [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 [Str "Code",Space,Str "Blocks"] +,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" ,Para [Str "And:"] ,CodeBlock ("",[],[]) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] +,Header 1 ("",[],[]) [Str "Lists"] +,Header 2 ("",[],[]) [Str "Unordered"] ,Para [Str "Asterisks",Space,Str "loose:"] ,BulletList [[Para [Str "asterisk",Space,Str "1"]] @@ -56,7 +56,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Para [Str "Minus",Space,Str "1"]] ,[Para [Str "Minus",Space,Str "2"]] ,[Para [Str "Minus",Space,Str "3"]]] -,Header 2 [Str "Ordered"] +,Header 2 ("",[],[]) [Str "Ordered"] ,OrderedList (1,Decimal,DefaultDelim) [[Para [Str "First"]] ,[Para [Str "Second"]] @@ -72,7 +72,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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."]]] -,Header 2 [Str "Nested"] +,Header 2 ("",[],[]) [Str "Nested"] ,BulletList [[Para [Str "Tab"] ,BulletList @@ -97,14 +97,14 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,[Para [Str "Fie"]] ,[Para [Str "Foe"]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Tabs",Space,Str "and",Space,Str "spaces"] +,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"]]]]] -,Header 2 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,Header 2 ("",[],[]) [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"] @@ -133,7 +133,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 1 [Str "Definition",Space,Str "Lists"] +,Header 1 ("",[],[]) [Str "Definition",Space,Str "Lists"] ,DefinitionList [([Str "apple"], [[Para [Str "red",Space,Str "fruit"]]]) @@ -169,7 +169,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,OrderedList (1,Decimal,DefaultDelim) [[Para [Str "sublist"]] ,[Para [Str "sublist"]]]]])] -,Header 1 [Str "Inline",Space,Str "Markup"] +,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 "."] @@ -182,14 +182,14 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a~b",Space,Str "c~d."] -,Header 1 [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"] +,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 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",Space,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 [Str "Special",Space,Str "Characters"] +,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"]] @@ -218,8 +218,8 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Bang:",Space,Str "!"] ,Para [Str "Plus:",Space,Str "+"] ,Para [Str "Minus:",Space,Str "-"] -,Header 1 [Str "Links"] -,Header 2 [Str "Explicit"] +,Header 1 ("",[],[]) [Str "Links"] +,Header 2 ("",[],[]) [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 "."] @@ -227,9 +227,9 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 [Code ("",[],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] +,Para [Link [Str "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] ,Para [Link [Str "Empty"] ("",""),Str "."] -,Header 2 [Str "Reference"] +,Header 2 ("",[],[]) [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 "."] @@ -242,34 +242,34 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,CodeBlock ("",[],[]) "[not]: /url" ,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."] ,Para [Str "Foo",Space,Link [Str "biz"] ("/url/",""),Str "."] -,Header 2 [Str "With",Space,Str "ampersands"] +,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:",Space,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",Space,Str "braces"] ("/script?foo=1&bar=2",""),Str "."] -,Header 2 [Str "Autolinks"] -,Para [Str "With",Space,Str "an",Space,Str "ampersand:",Space,Link [Code ("",[],[]) "http://example.com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")] +,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 [Code ("",[],[]) "http://example.com/"] ("http://example.com/","")]] + ,[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 [Code ("",[],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] +,Para [Str "An",Space,Str "e-mail",Space,Str "address:",Space,Link [Str "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] ,BlockQuote - [Para [Str "Blockquoted:",Space,Link [Code ("",[],[]) "http://example.com/"] ("http://example.com/","")]] + [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 ("",[],[]) "<http://example.com/>"] ,CodeBlock ("",[],[]) "or here: <http://example.com/>" -,Header 1 [Str "Images"] +,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):"] ,Para [Image [Str "lalune"] ("lalune.jpg","")] ,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [] ("movie.jpg",""),Space,Str "icon."] -,Header 1 [Str "Footnotes"] +,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",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",Space,Str "footnote",Space,Str "(as",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",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",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,",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",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",Space,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",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 [Str "Tables"] +,Header 1 ("",[],[]) [Str "Tables"] ,Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption:"] ,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] [AlignRight,AlignLeft,AlignCenter,AlignLeft] [0.0,0.0,0.0,0.0] [[Plain [Str "Right"]] diff --git a/tests/fb2.basic.fb2 b/tests/fb2.basic.fb2 new file mode 100644 index 000000000..14b03fbea --- /dev/null +++ b/tests/fb2.basic.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Top-level title</p></title><section><title><p>Section</p></title><section><title><p>Subsection</p></title><p>This <emphasis>emphasized</emphasis> <strong>strong</strong> <code>verbatim</code> markdown. See this link<a l:href="#l1" type="note"><sup>[1]</sup></a>.</p><p>Ordered list:</p><p> 1. one</p><p> 2. two</p><p> 3. three</p><cite><p>Blockquote is for citatons.</p></cite><empty-line /><p><code>Code</code></p><p><code>block</code></p><p><code>is</code></p><p><code>for</code></p><p><code>code.</code></p><empty-line /><p><strikethrough>Strikeout</strikethrough> is Pandoc's extension. Superscript and subscripts too: H<sub>2</sub>O is a liquid<a l:href="#n2" type="note"><sup>[2]</sup></a>. 2<sup>10</sup> is 1024.</p><p>Math is another Pandoc extension: <code>E = m c^2</code>.</p></section></section></section></body><body name="notes"><section id="l1"><title><p>1</p></title><p><code>http://example.com/</code></p></section><section id="n2"><title><p>2</p></title><p>Sometimes.</p></section></body></FictionBook>
\ No newline at end of file diff --git a/tests/fb2.basic.markdown b/tests/fb2.basic.markdown new file mode 100644 index 000000000..b798b13a4 --- /dev/null +++ b/tests/fb2.basic.markdown @@ -0,0 +1,33 @@ +# 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. diff --git a/tests/fb2.images-embedded.fb2 b/tests/fb2.images-embedded.fb2 new file mode 100644 index 000000000..1954232da --- /dev/null +++ b/tests/fb2.images-embedded.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p><image l:href="#image1" l:type="inlineImageType" alt="This image was embedded using data URI scheme" /></p><p>This image was embedded using data URI scheme</p></section></body><binary id="image1" content-type="image/png">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==</binary></FictionBook> diff --git a/tests/fb2.images-embedded.html b/tests/fb2.images-embedded.html new file mode 100644 index 000000000..19c8f7c7a --- /dev/null +++ b/tests/fb2.images-embedded.html @@ -0,0 +1,14 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta http-equiv="Content-Style-Type" content="text/css" /> + <meta name="generator" content="pandoc" /> + <title></title> +</head> +<body> +<div class="figure"> +<img src="data:image/png;base64,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==" alt="This image was embedded using data URI scheme" /><p class="caption">This image was embedded using data URI scheme</p> +</div> +</body> +</html> diff --git a/tests/fb2.images.fb2 b/tests/fb2.images.fb2 new file mode 100644 index 000000000..8b783edf5 --- /dev/null +++ b/tests/fb2.images.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p>This example test if Pandoc correctly embeds images into FictionBook.</p><p>Small inline image: <image l:href="#image1" l:type="inlineImageType" alt="alt text a small PNG image" />.</p><p>Paragraph image:</p><image l:href="#image2" l:type="imageType" alt="alt text of a big JPEG image" title="image title text" /><p>alt text of a big missing image</p><p>A missing image inline: alt text of missing image.</p></section></body><binary id="image2" content-type="image/jpeg">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</binary><binary id="image1" content-type="image/png">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</binary></FictionBook>
\ No newline at end of file diff --git a/tests/fb2.images.markdown b/tests/fb2.images.markdown new file mode 100644 index 000000000..419be7c44 --- /dev/null +++ b/tests/fb2.images.markdown @@ -0,0 +1,13 @@ +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 diff --git a/tests/fb2.math.fb2 b/tests/fb2.math.fb2 new file mode 100644 index 000000000..5a69556c1 --- /dev/null +++ b/tests/fb2.math.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p>List math:</p><p>• <code>E = m c^2</code></p><p>• <code>A = \pi r^2</code></p><p>Inline math: <code>x=\frac{-b \pm \sqrt {b^2-4ac}}{2a}</code>.</p><p>Display math:</p><code>\int_a^b \! f(x)\,dx = F(b) - F(a).</code></section></body></FictionBook>
\ No newline at end of file diff --git a/tests/fb2.math.markdown b/tests/fb2.math.markdown new file mode 100644 index 000000000..a88fb6cf1 --- /dev/null +++ b/tests/fb2.math.markdown @@ -0,0 +1,10 @@ +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).$$ diff --git a/tests/fb2.test-small.png b/tests/fb2.test-small.png Binary files differnew file mode 100644 index 000000000..16e177219 --- /dev/null +++ b/tests/fb2.test-small.png diff --git a/tests/fb2.test.jpg b/tests/fb2.test.jpg Binary files differnew file mode 100644 index 000000000..99d57db17 --- /dev/null +++ b/tests/fb2.test.jpg diff --git a/tests/fb2.titles.fb2 b/tests/fb2.titles.fb2 new file mode 100644 index 000000000..d8fc1e424 --- /dev/null +++ b/tests/fb2.titles.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Simple title</p></title><p>This example tests if Pandoc doesn't insert forbidden elements in FictionBook titles.</p></section><section><title><p>Emphasized Strong Title</p></title></section><section><title><p>Title with</p><empty-line /><p>line break</p></title></section></body></FictionBook>
\ No newline at end of file diff --git a/tests/fb2.titles.markdown b/tests/fb2.titles.markdown new file mode 100644 index 000000000..cc3d0e0d0 --- /dev/null +++ b/tests/fb2.titles.markdown @@ -0,0 +1,10 @@ +# Simple title + +This example tests if Pandoc doesn't insert forbidden elements in FictionBook titles. + +# *Emphasized* **Strong** Title + +# Title with\ +line break + + diff --git a/tests/html-reader.native b/tests/html-reader.native index a9070adc6..76c13c0b9 100644 --- a/tests/html-reader.native +++ b/tests/html-reader.native @@ -1,25 +1,25 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [], docDate = []}) [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Str ".",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber",Str "'",Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."] ,HorizontalRule -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] -,Header 3 [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 1 [Str "Level",Space,Str "1"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 3 [Str "Level",Space,Str "3"] +,Header 1 ("",[],[]) [Str "Headers"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] +,Header 3 ("",[],[]) [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 1 ("",[],[]) [Str "Level",Space,Str "1"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] +,Header 3 ("",[],[]) [Str "Level",Space,Str "3"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] -,Header 2 [Str "Level",Space,Str "2"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] ,HorizontalRule -,Header 1 [Str "Paragraphs"] +,Header 1 ("",[],[]) [Str "Paragraphs"] ,Para [Str "Here",Str "'",Str "s",Space,Str "a",Space,Str "regular",Space,Str "paragraph",Str "."] ,Para [Str "In",Space,Str "Markdown",Space,Str "1",Str ".",Str "0",Str ".",Str "0",Space,Str "and",Space,Str "earlier",Str ".",Space,Str "Version",Space,Str "8",Str ".",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item",Str ".",Space,Str "Because",Space,Str "a",Space,Str "hard",Str "-",Str "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",Str "."] ,Para [Str "Here",Str "'",Str "s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet",Str ".",Space,Str "*",Space,Str "criminey",Str "."] ,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "hard",Space,Str "line",Space,Str "break",LineBreak,Space,Str "here",Str "."] ,HorizontalRule -,Header 1 [Str "Block",Space,Str "Quotes"] +,Header 1 ("",[],[]) [Str "Block",Space,Str "Quotes"] ,Para [Str "E",Str "-",Str "mail",Space,Str "style:"] ,BlockQuote [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "block",Space,Str "quote",Str ".",Space,Str "It",Space,Str "is",Space,Str "pretty",Space,Str "short",Str "."]] @@ -51,14 +51,14 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [Para [Str "Don",Str "'",Str "t",Space,Str "quote",Space,Str "me",Str "."]]] ,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph",Str "."] ,HorizontalRule -,Header 1 [Str "Code",Space,Str "Blocks"] +,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" ,Para [Str "And:"] ,CodeBlock ("",[],[]) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" ,HorizontalRule -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] +,Header 1 ("",[],[]) [Str "Lists"] +,Header 2 ("",[],[]) [Str "Unordered"] ,Para [Str "Asterisks",Space,Str "tight:"] ,BulletList [[Plain [Str "asterisk",Space,Str "1"]] @@ -89,7 +89,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Para [Str "Minus",Space,Str "1"]] ,[Para [Str "Minus",Space,Str "2"]] ,[Para [Str "Minus",Space,Str "3"]]] -,Header 2 [Str "Ordered"] +,Header 2 ("",[],[]) [Str "Ordered"] ,Para [Str "Tight:"] ,OrderedList (1,DefaultStyle,DefaultDelim) [[Plain [Str "First"]] @@ -116,7 +116,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Item",Space,Str "1",Str ".",Space,Str "graf",Space,Str "two",Str ".",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",Str "'",Str "s",Space,Str "back",Str "."]] ,[Para [Str "Item",Space,Str "2",Str "."]] ,[Para [Str "Item",Space,Str "3",Str "."]]] -,Header 2 [Str "Nested"] +,Header 2 ("",[],[]) [Str "Nested"] ,BulletList [[Plain [Str "Tab"] ,BulletList @@ -141,14 +141,14 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,[Plain [Str "Fie"]] ,[Plain [Str "Foe"]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Tabs",Space,Str "and",Space,Str "spaces"] +,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"]]]]] -,Header 2 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,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"] @@ -175,7 +175,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,OrderedList (1,DefaultStyle,DefaultDelim) [[Plain [Str "Nested",Str "."]]]]] ,HorizontalRule -,Header 2 [Str "Definition"] +,Header 2 ("",[],[]) [Str "Definition"] ,DefinitionList [([Str "Violin"], [[Plain [Str "Stringed",Space,Str "musical",Space,Str "instrument",Str "."]] @@ -183,7 +183,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,([Str "Cello",LineBreak,Str "Violoncello"], [[Plain [Str "Low",Str "-",Str "voiced",Space,Str "stringed",Space,Str "instrument",Str "."]]])] ,HorizontalRule -,Header 1 [Str "Inline",Space,Str "Markup"] +,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 "."] @@ -193,7 +193,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Str "."] ,Para [Str "This",Space,Str "is",Space,Str "code:",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",Space,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",Space,Code ("",[],[]) "<html>",Str "."] ,HorizontalRule -,Header 1 [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"] +,Header 1 ("",[],[]) [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"] ,Para [Str "\"",Str "Hello,",Str "\"",Space,Str "said",Space,Str "the",Space,Str "spider",Str ".",Space,Str "\"",Str "'",Str "Shelob",Str "'",Space,Str "is",Space,Str "my",Space,Str "name",Str ".",Str "\""] ,Para [Str "'",Str "A",Str "'",Str ",",Space,Str "'",Str "B",Str "'",Str ",",Space,Str "and",Space,Str "'",Str "C",Str "'",Space,Str "are",Space,Str "letters",Str "."] ,Para [Str "'",Str "Oak,",Str "'",Space,Str "'",Str "elm,",Str "'",Space,Str "and",Space,Str "'",Str "beech",Str "'",Space,Str "are",Space,Str "names",Space,Str "of",Space,Str "trees",Str ".",Space,Str "So",Space,Str "is",Space,Str "'",Str "pine",Str ".",Str "'"] @@ -203,7 +203,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Dashes",Space,Str "between",Space,Str "numbers:",Space,Str "5",Str "-",Str "7,",Space,Str "255",Str "-",Str "66,",Space,Str "1987",Str "-",Str "1999",Str "."] ,Para [Str "Ellipses",Str ".",Str ".",Str ".",Str "and",Str ".",Space,Str ".",Space,Str ".",Str "and",Space,Str ".",Space,Str ".",Space,Str ".",Space,Str "."] ,HorizontalRule -,Header 1 [Str "LaTeX"] +,Header 1 ("",[],[]) [Str "LaTeX"] ,BulletList [[Plain [Str "\\cite[22",Str "-",Str "23]{smith",Str ".",Str "1899}"]] ,[Plain [Str "\\doublespacing"]] @@ -222,7 +222,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Here",Str "'",Str "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 [Str "Special",Space,Str "Characters"] +,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"]] @@ -252,8 +252,8 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Plus:",Space,Str "+"] ,Para [Str "Minus:",Space,Str "-"] ,HorizontalRule -,Header 1 [Str "Links"] -,Header 2 [Str "Explicit"] +,Header 1 ("",[],[]) [Str "Links"] +,Header 2 ("",[],[]) [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 "."] @@ -262,7 +262,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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",Str ".",Str "net)"] ,Para [Link [Str "Empty"] ("",""),Str "."] -,Header 2 [Str "Reference"] +,Header 2 ("",[],[]) [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 "."] @@ -275,12 +275,12 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 [Str "With",Space,Str "ampersands"] +,Header 2 ("",[],[]) [Str "With",Space,Str "ampersands"] ,Para [Str "Here",Str "'",Str "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",Str "'",Str "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",Str "'",Str "s",Space,Str "an",Space,Link [Str "inline",Space,Str "link"] ("/script?foo=1&bar=2",""),Str "."] ,Para [Str "Here",Str "'",Str "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 [Str "Autolinks"] +,Header 2 ("",[],[]) [Str "Autolinks"] ,Para [Str "With",Space,Str "an",Space,Str "ampersand:",Space,Link [Str "http://example",Str ".",Str "com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")] ,BulletList [[Plain [Str "In",Space,Str "a",Space,Str "list?"]] @@ -292,12 +292,12 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Auto",Str "-",Str "links",Space,Str "should",Space,Str "not",Space,Str "occur",Space,Str "here:",Space,Code ("",[],[]) "<http://example.com/>"] ,CodeBlock ("",[],[]) "or here: <http://example.com/>" ,HorizontalRule -,Header 1 [Str "Images"] +,Header 1 ("",[],[]) [Str "Images"] ,Para [Str "From",Space,Str "\"",Str "Voyage",Space,Str "dans",Space,Str "la",Space,Str "Lune",Str "\"",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",Str "."] ,HorizontalRule -,Header 1 [Str "Footnotes"] +,Header 1 ("",[],[]) [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)",Str "."] ,Para [Link [Str "(1)"] ("#ref_1",""),Space,Str "Here",Space,Str "is",Space,Str "the",Space,Str "footnote",Str ".",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",Str "."] ,Para [Link [Str "(longnote)"] ("#ref_longnote",""),Space,Str "Here",Str "'",Str "s",Space,Str "the",Space,Str "other",Space,Str "note",Str ".",Space,Str "This",Space,Str "one",Space,Str "contains",Space,Str "multiple",Space,Str "blocks",Str "."] diff --git a/tests/latex-reader.latex b/tests/latex-reader.latex index cd424baec..a1a4909d5 100644 --- a/tests/latex-reader.latex +++ b/tests/latex-reader.latex @@ -155,6 +155,11 @@ And: 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} @@ -775,7 +780,7 @@ With an ampersand: \url{http://example.com/?foo=1&bar=2} It should. \end{itemize} An e-mail address: -\href{mailto:nobody@nowhere.net}{\texttt{nobody@nowhere.net}} +\href{mailto:nobody@nowhere.net}{nobody@nowhere.net} \begin{quote} Blockquoted: \url{http://example.com/} diff --git a/tests/latex-reader.native b/tests/latex-reader.native index 2873529ae..014852249 100644 --- a/tests/latex-reader.native +++ b/tests/latex-reader.native @@ -2,25 +2,25 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [RawBlock "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",Space,Str "John",Space,Str "Gruber\8217s",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,HorizontalRule -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] -,Header 3 [Str "Level",Space,Str "3",Space,Str "with",Space,Emph [Str "emphasis"]] +,Header 1 ("",[],[]) [Str "Headers"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] +,Header 3 ("",[],[]) [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 [Str "Level",Space,Str "1"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 3 [Str "Level",Space,Str "3"] +,Header 1 ("",[],[]) [Str "Level",Space,Str "1"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] +,Header 3 ("",[],[]) [Str "Level",Space,Str "3"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] -,Header 2 [Str "Level",Space,Str "2"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] ,HorizontalRule -,Header 1 [Str "Paragraphs"] +,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.",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\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"] +,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."]] @@ -52,14 +52,15 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [Para [Str "Don\8217t",Space,Str "quote",Space,Str "me."]]] ,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph."] ,HorizontalRule -,Header 1 [Str "Code",Space,Str "Blocks"] +,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" ,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"]] ,HorizontalRule -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] +,Header 1 ("",[],[]) [Str "Lists"] +,Header 2 ("",[],[]) [Str "Unordered"] ,Para [Str "Asterisks",Space,Str "tight:"] ,BulletList [[Para [Str "asterisk",Space,Str "1"]] @@ -90,7 +91,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Para [Str "Minus",Space,Str "1"]] ,[Para [Str "Minus",Space,Str "2"]] ,[Para [Str "Minus",Space,Str "3"]]] -,Header 2 [Str "Ordered"] +,Header 2 ("",[],[]) [Str "Ordered"] ,Para [Str "Tight:"] ,OrderedList (1,Decimal,Period) [[Para [Str "First"]] @@ -117,7 +118,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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."]]] -,Header 2 [Str "Nested"] +,Header 2 ("",[],[]) [Str "Nested"] ,BulletList [[Para [Str "Tab"] ,BulletList @@ -142,14 +143,14 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,[Para [Str "Fie"]] ,[Para [Str "Foe"]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Tabs",Space,Str "and",Space,Str "spaces"] +,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"]]]]] -,Header 2 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,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"] @@ -179,7 +180,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "M.A.",Space,Str "2007"] ,Para [Str "B.",Space,Str "Williams"] ,HorizontalRule -,Header 1 [Str "Definition",Space,Str "Lists"] +,Header 1 ("",[],[]) [Str "Definition",Space,Str "Lists"] ,Para [Str "Tight",Space,Str "using",Space,Str "spaces:"] ,DefinitionList [([Str "apple"], @@ -214,7 +215,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,CodeBlock ("",[],[]) "{ orange code block }" ,BlockQuote [Para [Str "orange",Space,Str "block",Space,Str "quote"]]]])] -,Header 1 [Str "HTML",Space,Str "Blocks"] +,Header 1 ("",[],[]) [Str "HTML",Space,Str "Blocks"] ,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line:"] ,Para [Str "foo",Space,Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation:"] ,Para [Str "foo",Space,Str "bar",Space,Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table:"] @@ -233,7 +234,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,CodeBlock ("",[],[]) "<hr />" ,Para [Str "Hr\8217s:"] ,HorizontalRule -,Header 1 [Str "Inline",Space,Str "Markup"] +,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 "."] @@ -247,7 +248,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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",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",Space,Str "unescaped",Space,Str "spaces:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a",Math InlineMath "\\sim",Str "b",Space,Str "c",Math InlineMath "\\sim",Str "d."] ,HorizontalRule -,Header 1 [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"] +,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."]] @@ -257,9 +258,9 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 [Str "LaTeX"] +,Header 1 ("",[],[]) [Str "LaTeX"] ,BulletList - [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str ",",Space,Str "22-23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [RawInline "latex" "\\cite[22-23]{smith.1899}"]]] + [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str ",",Space,Str "22-23"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [RawInline "latex" "\\cite[22-23]{smith.1899}"]]] ,[Para [RawInline "latex" "\\doublespacing"]] ,[Para [Math InlineMath "2+2=4"]] ,[Para [Math InlineMath "x \\in y"]] @@ -287,7 +288,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[[Plain [Str "Animal"]]] ,[[Plain [Str "Vegetable"]]]] ,HorizontalRule -,Header 1 [Str "Special",Space,Str "Characters"] +,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"]] @@ -301,7 +302,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 "Backtick:",Space,Str "\8216"] ,Para [Str "Asterisk:",Space,Str "*"] ,Para [Str "Underscore:",Space,Str "_"] ,Para [Str "Left",Space,Str "brace:",Space,Str "{"] @@ -317,8 +318,8 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,Para [Str "Plus:",Space,Str "+"] ,Para [Str "Minus:",Space,Str "-"] ,HorizontalRule -,Header 1 [Str "Links"] -,Header 2 [Str "Explicit"] +,Header 1 ("",[],[]) [Str "Links"] +,Header 2 ("",[],[]) [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 "."] @@ -328,7 +329,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,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 [Str "Reference"] +,Header 2 ("",[],[]) [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 "."] @@ -341,29 +342,29 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,CodeBlock ("",[],[]) "[not]: /url" ,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."] ,Para [Str "Foo",Space,Link [Str "biz"] ("/url/",""),Str "."] -,Header 2 [Str "With",Space,Str "ampersands"] +,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:",Space,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",Space,Str "braces"] ("/script?foo=1&bar=2",""),Str "."] -,Header 2 [Str "Autolinks"] -,Para [Str "With",Space,Str "an",Space,Str "ampersand:",Space,Link [Code ("",["url"],[]) "http://example.com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")] +,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 [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] + ,[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 [Code ("",[],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] +,Para [Str "An",Space,Str "e-mail",Space,Str "address:",Space,Link [Str "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] ,BlockQuote - [Para [Str "Blockquoted:",Space,Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] + [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 ("",[],[]) "<http://example.com/>"] ,CodeBlock ("",[],[]) "or here: <http://example.com/>" ,HorizontalRule -,Header 1 [Str "Images"] +,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):"] ,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."] ,HorizontalRule -,Header 1 [Str "Footnotes"] +,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",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",Space,Str "footnote",Space,Str "(as",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",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",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,",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",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",Space,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",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."]]]] diff --git a/tests/lhs-test-markdown.native b/tests/lhs-test-markdown.native new file mode 100644 index 000000000..28275f3eb --- /dev/null +++ b/tests/lhs-test-markdown.native @@ -0,0 +1,8 @@ +[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",Space,Str "return",Space,Str "a",Space,Str "single",Space,Str "value:"] +,CodeBlock ("",["sourceCode","literate","haskell"],[]) "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",Space,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",Space,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"]]] diff --git a/tests/lhs-test.html b/tests/lhs-test.html index 955adcdd7..a0d05055e 100644 --- a/tests/lhs-test.html +++ b/tests/lhs-test.html @@ -5,10 +5,11 @@ <meta http-equiv="Content-Style-Type" content="text/css" /> <meta name="generator" content="pandoc" /> <title></title> + <style type="text/css">code{white-space: pre;}</style> <style type="text/css"> table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { margin: 0; padding: 0; vertical-align: baseline; border: none; } -table.sourceCode { width: 100%; } +table.sourceCode { width: 100%; line-height: 100%; } td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } td.sourceCode { padding-left: 5px; } code > span.kw { color: #007020; font-weight: bold; } @@ -26,7 +27,7 @@ code > span.er { color: #ff0000; font-weight: bold; } </style> </head> <body> -<h1 id="lhs-test">lhs test</h1> +<h1>lhs test</h1> <p><code>unsplit</code> is an arrow that takes a pair of values and combines them to return a single value:</p> <pre class="sourceCode literate haskell"><code class="sourceCode haskell"><span class="ot">unsplit ::</span> (<span class="dt">Arrow</span> a) <span class="ot">=></span> (b <span class="ot">-></span> c <span class="ot">-></span> d) <span class="ot">-></span> a (b, c) d unsplit <span class="fu">=</span> arr <span class="fu">.</span> <span class="fu">uncurry</span> diff --git a/tests/lhs-test.html+lhs b/tests/lhs-test.html+lhs index ea6fa3d48..347009c5a 100644 --- a/tests/lhs-test.html+lhs +++ b/tests/lhs-test.html+lhs @@ -5,10 +5,11 @@ <meta http-equiv="Content-Style-Type" content="text/css" /> <meta name="generator" content="pandoc" /> <title></title> + <style type="text/css">code{white-space: pre;}</style> <style type="text/css"> table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { margin: 0; padding: 0; vertical-align: baseline; border: none; } -table.sourceCode { width: 100%; } +table.sourceCode { width: 100%; line-height: 100%; } td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } td.sourceCode { padding-left: 5px; } code > span.kw { color: #007020; font-weight: bold; } @@ -26,7 +27,7 @@ code > span.er { color: #ff0000; font-weight: bold; } </style> </head> <body> -<h1 id="lhs-test">lhs test</h1> +<h1>lhs test</h1> <p><code>unsplit</code> is an arrow that takes a pair of values and combines them to return a single value:</p> <pre class="sourceCode literate haskell"><code class="sourceCode haskell"><span class="fu">></span><span class="ot"> unsplit ::</span> (<span class="dt">Arrow</span> a) <span class="ot">=></span> (b <span class="ot">-></span> c <span class="ot">-></span> d) <span class="ot">-></span> a (b, c) d <span class="fu">></span> unsplit <span class="fu">=</span> arr <span class="fu">.</span> <span class="fu">uncurry</span> diff --git a/tests/lhs-test.latex b/tests/lhs-test.latex index acfb86679..b0d585261 100644 --- a/tests/lhs-test.latex +++ b/tests/lhs-test.latex @@ -6,6 +6,8 @@ \usepackage{fixltx2e} % provides \textsubscript % use microtype if available \IfFileExists{microtype.sty}{\usepackage{microtype}}{} +% use upquote if available, for straight quotes in verbatim environments +\IfFileExists{upquote.sty}{\usepackage{upquote}}{} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex \usepackage[utf8]{inputenc} \else % if luatex or xelatex @@ -70,7 +72,7 @@ return a single value: \begin{Highlighting}[] \OtherTok{unsplit ::} \NormalTok{(}\DataTypeTok{Arrow} \NormalTok{a) }\OtherTok{=>} \NormalTok{(b }\OtherTok{->} \NormalTok{c }\OtherTok{->} \NormalTok{d) }\OtherTok{->} \NormalTok{a (b, c) d} \NormalTok{unsplit }\FunctionTok{=} \NormalTok{arr }\FunctionTok{.} \FunctionTok{uncurry} - \CommentTok{-- arr (\textbackslash{}op (x,y) -> x {\char18}op{\char18} y) } + \CommentTok{-- arr (\textbackslash{}op (x,y) -> x `op` y) } \end{Highlighting} \end{Shaded} diff --git a/tests/lhs-test.latex+lhs b/tests/lhs-test.latex+lhs index ba0c67d48..0e0744b3c 100644 --- a/tests/lhs-test.latex+lhs +++ b/tests/lhs-test.latex+lhs @@ -6,6 +6,8 @@ \usepackage{fixltx2e} % provides \textsubscript % use microtype if available \IfFileExists{microtype.sty}{\usepackage{microtype}}{} +% use upquote if available, for straight quotes in verbatim environments +\IfFileExists{upquote.sty}{\usepackage{upquote}}{} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex \usepackage[utf8]{inputenc} \else % if luatex or xelatex diff --git a/tests/lhs-test.native b/tests/lhs-test.native index 4b5a3e112..dc6c0588e 100644 --- a/tests/lhs-test.native +++ b/tests/lhs-test.native @@ -1,8 +1,8 @@ -[Header 1 [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",Space,Str "return",Space,Str "a",Space,Str "single",Space,Str "value",Str ":"] +[Header 1 ("",[],[]) [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",Space,Str "return",Space,Str "a",Space,Str "single",Space,Str "value:"] ,CodeBlock ("",["sourceCode","literate","haskell"],[]) "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",Space,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",Space,Str "second",Space,Str "item",Space,Str "of",Space,Str "the",Space,Str "pair)",Str "."] +,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",Space,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",Space,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",Str ":"] +,Para [Str "Block",Space,Str "quote:"] ,BlockQuote [Para [Str "foo",Space,Str "bar"]]] diff --git a/tests/markdown-reader-more.native b/tests/markdown-reader-more.native index a24e48e86..e76b12918 100644 --- a/tests/markdown-reader-more.native +++ b/tests/markdown-reader-more.native @@ -1,54 +1,54 @@ -[Header 1 [Str "Additional",Space,Str "markdown",Space,Str "reader",Space,Str "tests"] -,Header 2 [Str "Blank",Space,Str "line",Space,Str "before",Space,Str "URL",Space,Str "in",Space,Str "link",Space,Str "reference"] +[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 [Str "Raw",Space,Str "ConTeXt",Space,Str "environments"] +,Header 2 ("raw-context-environments",[],[]) [Str "Raw",Space,Str "ConTeXt",Space,Str "environments"] ,Plain [RawInline "tex" "\\placeformula "] ,RawBlock "context" "\\startformula\n L_{1} = L_{2}\n \\stopformula" ,RawBlock "context" "\\start[a2]\n\\start[a2]\n\\stop[a2]\n\\stop[a2]" -,Header 2 [Str "URLs",Space,Str "with",Space,Str "spaces"] +,Header 2 ("urls-with-spaces",[],[]) [Str "URLs",Space,Str "with",Space,Str "spaces"] ,Para [Link [Str "foo"] ("/bar%20and%20baz",""),Space,Link [Str "foo"] ("/bar%20and%20baz",""),Space,Link [Str "foo"] ("/bar%20and%20baz",""),Space,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")] -,Header 2 [Str "Horizontal",Space,Str "rules",Space,Str "with",Space,Str "spaces",Space,Str "at",Space,Str "end"] +,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 [Str "Raw",Space,Str "HTML",Space,Str "before",Space,Str "header"] -,Plain [RawInline "html" "<a>",RawInline "html" "</a>"] -,Header 3 [Str "my",Space,Str "header"] -,Header 2 [Str "$",Space,Str "in",Space,Str "math"] +,Header 2 ("raw-html-before-header",[],[]) [Str "Raw",Space,Str "HTML",Space,Str "before",Space,Str "header"] +,Para [RawInline "html" "<a>",RawInline "html" "</a>"] +,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"] -,Header 2 [Str "Commented",Str "-",Str "out",Space,Str "list",Space,Str "item"] +,Header 2 ("commented-out-list-item",[],[]) [Str "Commented-out",Space,Str "list",Space,Str "item"] ,BulletList [[Plain [Str "one",Space,RawInline "html" "<!--\n- two\n-->"]] ,[Plain [Str "three"]]] -,Header 2 [Str "Backslash",Space,Str "newline"] +,Header 2 ("backslash-newline",[],[]) [Str "Backslash",Space,Str "newline"] ,Para [Str "hi",LineBreak,Str "there"] -,Header 2 [Str "Code",Space,Str "spans"] +,Header 2 ("code-spans",[],[]) [Str "Code",Space,Str "spans"] ,Para [Code ("",[],[]) "hi\\"] ,Para [Code ("",[],[]) "hi there"] ,Para [Code ("",[],[]) "hi````there"] -,Para [Str "`",Str "hi"] -,Para [Str "there",Str "`"] -,Header 2 [Str "Multilingual",Space,Str "URLs"] -,Plain [RawInline "html" "<http://\27979.com?\27979=\27979>"] +,Para [Str "`hi"] +,Para [Str "there`"] +,Header 2 ("multilingual-urls",[],[]) [Str "Multilingual",Space,Str "URLs"] +,Para [Link [Str "http://\27979.com?\27979=\27979"] ("http://\27979.com?\27979=\27979","")] ,Para [Link [Str "foo"] ("/bar/\27979?x=\27979","title")] -,Para [Link [Code ("",["url"],[]) "\27979@foo.\27979.baz"] ("mailto:\27979@foo.\27979.baz","")] -,Header 2 [Str "Numbered",Space,Str "examples"] +,Para [Link [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",Str "."]] - ,[Plain [Str "Second",Space,Str "example",Str "."]]] -,Para [Str "Explanation",Space,Str "of",Space,Str "examples",Space,Str "(",Str "2",Str ")",Space,Str "and",Space,Str "(",Str "3",Str ")",Str "."] + [[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",Str "."]]] -,Header 2 [Str "Macros"] + [[Plain [Str "Third",Space,Str "example."]]] +,Header 2 ("macros",[],[]) [Str "Macros"] ,Para [Math InlineMath "\\langle x,y \\rangle"] -,Header 2 [Str "Case",Str "-",Str "insensitive",Space,Str "references"] +,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 [Str "Curly",Space,Str "smart",Space,Str "quotes"] +,Header 2 ("curly-smart-quotes",[],[]) [Str "Curly",Space,Str "smart",Space,Str "quotes"] ,Para [Quoted DoubleQuote [Str "Hi"]] ,Para [Quoted SingleQuote [Str "Hi"]] -,Header 2 [Str "Consecutive",Space,Str "lists"] +,Header 2 ("consecutive-lists",[],[]) [Str "Consecutive",Space,Str "lists"] ,BulletList [[Plain [Str "one"]] ,[Plain [Str "two"]]] @@ -57,4 +57,14 @@ ,[Plain [Str "two"]]] ,OrderedList (1,LowerAlpha,Period) [[Plain [Str "one"]] - ,[Plain [Str "two"]]]] + ,[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 "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"] +,Header 2 ("line-blocks",[],[]) [Str "Line",Space,Str "blocks"] +,Para [Str "But",Space,Str "can",Space,Str "a",Space,Str "bee",Space,Str "be",Space,Str "said",Space,Str "to",Space,Str "be",LineBreak,Str "\160\160\160\160or",Space,Str "not",Space,Str "to",Space,Str "be",Space,Str "an",Space,Str "entire",Space,Str "bee,",LineBreak,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,",LineBreak,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?"] +,Para [Str "Continuation",Space,Str "line",LineBreak,Str "\160\160and",Space,Str "another"]] diff --git a/tests/markdown-reader-more.txt b/tests/markdown-reader-more.txt index b99bb3121..b8ff74f26 100644 --- a/tests/markdown-reader-more.txt +++ b/tests/markdown-reader-more.txt @@ -133,3 +133,32 @@ $\tuple{x,y}$ a. one b. two + +## Implicit header references + +### My header + +### My other header + +A link to [My header]. + +Another link to [it][My header]. + +[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"} + +## 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 + diff --git a/tests/mediawiki-reader.native b/tests/mediawiki-reader.native new file mode 100644 index 000000000..a3dc2a08c --- /dev/null +++ b/tests/mediawiki-reader.native @@ -0,0 +1,241 @@ +Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) +[Header 1 ("",[],[]) [Str "header"] +,Header 2 ("",[],[]) [Str "header",Space,Str "level",Space,Str "two"] +,Header 3 ("",[],[]) [Str "header",Space,Str "level",Space,Str "3"] +,Header 4 ("",[],[]) [Str "header",Space,Emph [Str "level"],Space,Str "four"] +,Header 5 ("",[],[]) [Str "header",Space,Str "level",Space,Str "5"] +,Header 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 ("",[],[]) [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 ("",[],[]) [Str "horizontal",Space,Str "rule"] +,Para [Str "top"] +,HorizontalRule +,Para [Str "bottom"] +,HorizontalRule +,Header 2 ("",[],[]) [Str "nowiki"] +,Para [Str "''not",Space,Str "emph''"] +,Header 2 ("",[],[]) [Str "strikeout"] +,Para [Strikeout [Str "This",Space,Str "is",Space,Emph [Str "struck",Space,Str "out"]]] +,Header 2 ("",[],[]) [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 ("",[],[]) [Str "comments"] +,Para [Str "inline",Space,Str "comment"] +,Para [Str "between",Space,Str "blocks"] +,Header 2 ("",[],[]) [Str "linebreaks"] +,Para [Str "hi",LineBreak,Str "there"] +,Para [Str "hi",LineBreak,Str "there"] +,Header 2 ("",[],[]) [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 ("",[],[]) [Str "p",Space,Str "tags"] +,Para [Str "hi",Space,Str "there"] +,Para [Str "bud"] +,Para [Str "another"] +,Header 2 ("",[],[]) [Str "raw",Space,Str "html"] +,Para [Str "hi",Space,RawInline "html" "<span style=\"color:red\">",Emph [Str "there"],RawInline "html" "</span>",Str "."] +,Para [RawInline "html" "<ins>",Str "inserted",RawInline "html" "</ins>"] +,RawBlock "html" "<div class=\"special\">" +,Para [Str "hi",Space,Emph [Str "there"]] +,RawBlock "html" "</div>" +,Header 2 ("",[],[]) [Str "sup,",Space,Str "sub,",Space,Str "del"] +,Para [Str "H",Subscript [Str "2"],Str "O",Space,Str "base",Superscript [Emph [Str "exponent"]],Space,Strikeout [Str "hello"]] +,Header 2 ("",[],[]) [Str "inline",Space,Str "code"] +,Para [Code ("",[],[]) "*\8594*",Space,Code ("",[],[]) "typed",Space,Code ("",["haskell"],[]) ">>="] +,Header 2 ("",[],[]) [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 ("",[],[]) [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 ("",[],[]) [Str "external",Space,Str "links"] +,Para [Link [Emph [Str "Google"],Space,Str "search",Space,Str "engine"] ("http://google.com","")] +,Para [Link [Str "http://johnmacfarlane.net/pandoc/"] ("http://johnmacfarlane.net/pandoc/","")] +,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 ("",[],[]) [Str "internal",Space,Str "links"] +,Para [Link [Str "Help"] ("Help","wikilink")] +,Para [Link [Str "the",Space,Str "help",Space,Str "page"] ("Help","wikilink")] +,Para [Link [Str "Helpers"] ("Help","wikilink")] +,Para [Link [Str "Help"] ("Help","wikilink"),Str "ers"] +,Para [Link [Str "Contents"] ("Help:Contents","wikilink")] +,Para [Link [Str "#My",Space,Str "anchor"] ("#My_anchor","wikilink")] +,Para [Link [Str "and",Space,Str "text"] ("Page#with_anchor","wikilink")] +,Header 2 ("",[],[]) [Str "images"] +,Para [Image [Str "caption"] ("example.jpg","image")] +,Para [Image [Str "the",Space,Emph [Str "caption"],Space,Str "with",Space,Link [Str "external",Space,Str "link"] ("http://google.com","")] ("example.jpg","image")] +,Para [Image [Str "caption"] ("example.jpg","image")] +,Para [Image [Str "example.jpg"] ("example.jpg","image")] +,Header 2 ("",[],[]) [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."] +,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",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"]] + ,[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 "<br/>"]]])]] + ,[Plain [RawInline "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 ("",[],[]) [Str "math"] +,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Math InlineMath "x=\\frac{y^\\pi}{z}",Str "."] +,Header 2 ("",[],[]) [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<nowiki>).",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 "html" "<hr/>" +,Para [Str "preformatted"] +,Header 2 ("",[],[]) [Str "templates"] +,RawBlock "mediawiki" "{{Welcome}}" +,RawBlock "mediawiki" "{{Foo:Bar}}" +,RawBlock "mediawiki" "{{Thankyou|all your effort|Me}}" +,Para [Str "Written",Space,RawInline "mediawiki" "{{{date}}}",Space,Str "by",Space,RawInline "mediawiki" "{{{name}}}",Str "."] +,Header 2 ("",[],[]) [Str "tables"] +,Table [] [AlignDefault,AlignDefault] [0.0,0.0] + [[] + ,[]] + [[[Para [Str "Orange"]] + ,[Para [Str "Apple"]]] + ,[[Para [Str "Bread"]] + ,[Para [Str "Pie"]]] + ,[[Para [Str "Butter"]] + ,[Para [Str "Ice",Space,Str "cream"]]]] +,Table [Str "Food",Space,Str "complements"] [AlignDefault,AlignDefault] [0.0,0.0] + [[Para [Str "Orange"]] + ,[Para [Str "Apple"]]] + [[[Para [Str "Bread"]] + ,[Para [Str "Pie"]]] + ,[[Para [Str "Butter"]] + ,[Para [Str "Ice",Space,Str "cream"]]]] +,Table [Str "Food",Space,Str "complements"] [AlignDefault,AlignDefault] [0.0,0.0] + [[Para [Str "Orange"]] + ,[Para [Str "Apple"]]] + [[[Para [Str "Bread"] + ,Para [Str "and",Space,Str "cheese"]] + ,[Para [Str "Pie"] + ,OrderedList (1,DefaultStyle,DefaultDelim) + [[Plain [Str "apple"]] + ,[Plain [Str "carrot"]]]]]] +,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] + [[] + ,[] + ,[]] + [[[Para [Str "Orange"]] + ,[Para [Str "Apple"]] + ,[Para [Str "more"]]] + ,[[Para [Str "Bread"]] + ,[Para [Str "Pie"]] + ,[Para [Str "more"]]] + ,[[Para [Str "Butter"]] + ,[Para [Str "Ice",Space,Str "cream"]] + ,[Para [Str "and",Space,Str "more"]]]] +,Table [] [AlignLeft,AlignRight,AlignCenter] [0.25,0.125,0.125] + [[Para [Str "Left"]] + ,[Para [Str "Right"]] + ,[Para [Str "Center"]]] + [[[Para [Str "left"]] + ,[Para [Str "15.00"]] + ,[Para [Str "centered"]]] + ,[[Para [Str "more"]] + ,[Para [Str "2.0"]] + ,[Para [Str "more"]]]] +,Table [] [AlignDefault,AlignDefault] [0.0,0.0] + [[] + ,[]] + [[[Para [Str "Orange"]] + ,[Para [Str "Apple"]]] + ,[[Para [Str "Bread"]] + ,[Table [] [AlignDefault,AlignDefault] [0.0,0.0] + [[Para [Str "fruit"]] + ,[Para [Str "topping"]]] + [[[Para [Str "apple"]] + ,[Para [Str "ice",Space,Str "cream"]]]]]] + ,[[Para [Str "Butter"]] + ,[Para [Str "Ice",Space,Str "cream"]]]] +,Header 2 ("",[],[]) [Str "notes"] +,Para [Str "My",Space,Str "note!",Note [Plain [Str "This."]]]] diff --git a/tests/mediawiki-reader.wiki b/tests/mediawiki-reader.wiki new file mode 100644 index 000000000..cc0ac7918 --- /dev/null +++ b/tests/mediawiki-reader.wiki @@ -0,0 +1,369 @@ += 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 == + +<nowiki>''not emph''</nowiki> + +== strikeout == + +<strike> This is ''struck out''</strike> + +== entities == + +hi & low + +hi & low + +Gödel + +̉પ + +== comments == + +inline<!-- secret --> comment + +<!-- secret --> + +between blocks + + <!-- secret --> + +== linebreaks == + +hi<br/>there + +hi<br> +there + +== : indents == + +hi +: there +bud + +hi +:: there +bud + +== p tags == + +hi there +<p> +bud +<p> +another +</p> + +== raw html == + +hi <span style="color:red">''there''</span>. + +<ins>inserted</ins> + +<div class="special"> +hi ''there'' +</div> + +== sup, sub, del == + +H<sub>2</sub>O base<sup>''exponent''</sup> +<del>hello</del> + +== inline code == + +<code>*→*</code> <tt>typed</tt> <hask>>>=</hask> + +== code blocks == + +<pre> +case xs of + (_:_) -> reverse xs + [] -> ['*'] +</pre> + +<haskell> +case xs of + (_:_) -> reverse xs + [] -> ['*'] +</haskell> + +<syntaxhighlight lang="ruby" line start=100> +widgets.each do |w| + print w.price +end +</syntaxhighlight> + +== block quotes == + +Regular paragraph +<blockquote> +This is a block quote. + +With two paragraphs. +</blockquote> +Nother paragraph. + +== external links == + +[http://google.com ''Google'' search engine] + +http://johnmacfarlane.net/pandoc/ + +[http://google.com] [http://yahoo.com] + +[mailto:info@example.org email me] + +== internal links == + +[[Help]] + +[[Help|the help page]] + +[[Help]]ers + +[[Help]]<nowiki/>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]] + +== lists == + +* Start each line +* with an asterisk (*). +** More asterisks gives deeper +*** and deeper levels. +* Line breaks<br/>don't break levels. +*** But jumping levels creates empty space. +Any other start ends the list. + +# Start each line +# with a number sign (#). +## More number signs gives deeper +### and deeper +### levels. +# Line breaks<br/>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<br/>of <nowiki><br/></nowiki> +# {{{template +|author=John +|title=My Book +}}} +## five sub 1 +### five sub 1 sub 1 +## five sub 2 + +<ol> + <li>list item ''emph'' + <ol> + <li>list item B1</li> + <li>list item B2</li> + </ol>continuing list item A1 + </li> + <li>list item A2</li> +</ol> + +<ul> +#abc +#def +#ghi +</ul> + +<ol start="9"> +<li>Amsterdam</li> +<li>Rotterdam</li> +<li>The Hague</li> +</ol> + +== math == + +Here is some <math>x=\frac{y^\pi}{z}</math>. + +== preformatted blocks == + + Start each line with a space. + Text is '''preformatted''' and + ''markups'' '''''can''''' be done. + + hell yeah + + <nowiki>Start with a space in the first column, +(before the <nowiki>). + +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</nowiki> + +Not<hr/> preformatted + +== 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 +|} + + +== notes == + +My note!<ref>This.</ref> + diff --git a/tests/pipe-tables.native b/tests/pipe-tables.native new file mode 100644 index 000000000..5420a7bd3 --- /dev/null +++ b/tests/pipe-tables.native @@ -0,0 +1,70 @@ +[Para [Str "Simplest",Space,Str "table",Space,Str "without",Space,Str "caption:"] +,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] + [[Plain [Str "Default1"]] + ,[Plain [Str "Default2"]] + ,[Plain [Str "Default3"]]] + [[[Plain [Str "12"]] + ,[Plain [Str "12"]] + ,[Plain [Str "12"]]] + ,[[Plain [Str "123"]] + ,[Plain [Str "123"]] + ,[Plain [Str "123"]]] + ,[[Plain [Str "1"]] + ,[Plain [Str "1"]] + ,[Plain [Str "1"]]]] +,Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption:"] +,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] [AlignRight,AlignLeft,AlignDefault,AlignCenter] [0.0,0.0,0.0,0.0] + [[Plain [Str "Right"]] + ,[Plain [Str "Left"]] + ,[Plain [Str "Default"]] + ,[Plain [Str "Center"]]] + [[[Plain [Str "12"]] + ,[Plain [Str "12"]] + ,[Plain [Str "12"]] + ,[Plain [Str "12"]]] + ,[[Plain [Str "123"]] + ,[Plain [Str "123"]] + ,[Plain [Str "123"]] + ,[Plain [Str "123"]]] + ,[[Plain [Str "1"]] + ,[Plain [Str "1"]] + ,[Plain [Str "1"]] + ,[Plain [Str "1"]]]] +,Para [Str "Simple",Space,Str "table",Space,Str "without",Space,Str "caption:"] +,Table [] [AlignRight,AlignLeft,AlignCenter] [0.0,0.0,0.0] + [[Plain [Str "Right"]] + ,[Plain [Str "Left"]] + ,[Plain [Str "Center"]]] + [[[Plain [Str "12"]] + ,[Plain [Str "12"]] + ,[Plain [Str "12"]]] + ,[[Plain [Str "123"]] + ,[Plain [Str "123"]] + ,[Plain [Str "123"]]] + ,[[Plain [Str "1"]] + ,[Plain [Str "1"]] + ,[Plain [Str "1"]]]] +,Para [Str "Headerless",Space,Str "table",Space,Str "without",Space,Str "caption:"] +,Table [] [AlignRight,AlignLeft,AlignCenter] [0.0,0.0,0.0] + [[] + ,[] + ,[]] + [[[Plain [Str "12"]] + ,[Plain [Str "12"]] + ,[Plain [Str "12"]]] + ,[[Plain [Str "123"]] + ,[Plain [Str "123"]] + ,[Plain [Str "123"]]] + ,[[Plain [Str "1"]] + ,[Plain [Str "1"]] + ,[Plain [Str "1"]]]] +,Para [Str "Table",Space,Str "without",Space,Str "sides:"] +,Table [] [AlignDefault,AlignRight] [0.0,0.0] + [[Plain [Str "Fruit"]] + ,[Plain [Str "Quantity"]]] + [[[Plain [Str "apple"]] + ,[Plain [Str "5"]]] + ,[[Plain [Str "orange"]] + ,[Plain [Str "17"]]] + ,[[Plain [Str "pear"]] + ,[Plain [Str "302"]]]]] diff --git a/tests/pipe-tables.txt b/tests/pipe-tables.txt new file mode 100644 index 000000000..929038ebb --- /dev/null +++ b/tests/pipe-tables.txt @@ -0,0 +1,42 @@ +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 + diff --git a/tests/rst-reader.native b/tests/rst-reader.native index bf794c849..895d06d69 100644 --- a/tests/rst-reader.native +++ b/tests/rst-reader.native @@ -2,123 +2,123 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ": [DefinitionList [([Str "Revision"], [[Para [Str "3"]]])] -,Header 1 [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",Str ".",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber",Str "\8217",Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."] -,Header 2 [Str "Level",Space,Str "two",Space,Str "header"] -,Header 3 [Str "Level",Space,Str "three"] -,Header 4 [Str "Level",Space,Str "four",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 5 [Str "Level",Space,Str "five"] -,Header 1 [Str "Paragraphs"] -,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "regular",Space,Str "paragraph",Str "."] -,Para [Str "In",Space,Str "Markdown",Space,Str "1",Str ".",Str "0",Str ".",Str "0",Space,Str "and",Space,Str "earlier",Str ".",Space,Str "Version",Space,Str "8",Str ".",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item",Str ".",Space,Str "Because",Space,Str "a",Space,Str "hard",Str "-",Str "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",Str "."] -,Para [Str "Here",Str "\8217",Str "s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet",Str ".",Space,Str "*",Space,Str "criminey",Str "."] -,Para [Str "Horizontal",Space,Str "rule",Str ":"] +,Header 1 ("",[],[]) [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",Space,Str "John",Space,Str "Gruber\8217s",Space,Str "markdown",Space,Str "test",Space,Str "suite."] +,Header 2 ("",[],[]) [Str "Level",Space,Str "two",Space,Str "header"] +,Header 3 ("",[],[]) [Str "Level",Space,Str "three"] +,Header 4 ("",[],[]) [Str "Level",Space,Str "four",Space,Str "with",Space,Emph [Str "emphasis"]] +,Header 5 ("",[],[]) [Str "Level",Space,Str "five"] +,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.",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\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet.",Space,Str "*",Space,Str "criminey."] +,Para [Str "Horizontal",Space,Str "rule:"] ,HorizontalRule -,Para [Str "Another",Str ":"] +,Para [Str "Another:"] ,HorizontalRule -,Header 1 [Str "Block",Space,Str "Quotes"] -,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "block",Space,Str "quote",Str ":"] +,Header 1 ("",[],[]) [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",Str ".",Space,Str "It",Space,Str "is",Space,Str "pretty",Space,Str "short",Str "."]] -,Para [Str "Here",Str "\8217",Str "s",Space,Str "another,",Space,Str "differently",Space,Str "indented",Str ":"] + [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."]] +,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",Str ".",Space,Str "It",Str "\8217",Str "s",Space,Str "indented",Space,Str "with",Space,Str "a",Space,Str "tab",Str "."] - ,Para [Str "Code",Space,Str "in",Space,Str "a",Space,Str "block",Space,Str "quote",Str ":"] + [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "block",Space,Str "quote.",Space,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",Str ":"] + ,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",Str ":"] + ,Para [Str "Nested",Space,Str "block",Space,Str "quotes:"] ,BlockQuote [Para [Str "nested"] ,BlockQuote [Para [Str "nested"]]]] -,Header 1 [Str "Code",Space,Str "Blocks"] -,Para [Str "Code",Str ":"] +,Header 1 ("",[],[]) [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",Str ":"] +,Para [Str "And:"] ,CodeBlock ("",[],[]) "this block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" -,Para [Str "And",Str ":"] +,Para [Str "And:"] ,CodeBlock ("",["sourceCode","python"],[]) "def my_function(x):\n return x + 1" -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] -,Para [Str "Asterisks",Space,Str "tight",Str ":"] +,Header 1 ("",[],[]) [Str "Lists"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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 [Str "Ordered"] -,Para [Str "Tight",Str ":"] +,Header 2 ("",[],[]) [Str "Ordered"] +,Para [Str "Tight:"] ,OrderedList (1,Decimal,Period) [[Plain [Str "First"]] ,[Plain [Str "Second"]] ,[Plain [Str "Third"]]] -,Para [Str "and",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,Para [Str "Multiple",Space,Str "paragraphs:"] ,OrderedList (1,Decimal,Period) - [[Para [Str "Item",Space,Str "1,",Space,Str "graf",Space,Str "one",Str "."] - ,Para [Str "Item",Space,Str "1",Str ".",Space,Str "graf",Space,Str "two",Str ".",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",Str "\8217",Str "s",Space,Str "back",Str "."]] - ,[Para [Str "Item",Space,Str "2",Str "."]] - ,[Para [Str "Item",Space,Str "3",Str "."]]] -,Para [Str "Nested",Str ":"] + [[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."]]] +,Para [Str "Nested:"] ,BulletList [[Para [Str "Tab"] ,BulletList [[Para [Str "Tab"] ,BulletList [[Plain [Str "Tab"]]]]]]] -,Para [Str "Here",Str "\8217",Str "s",Space,Str "another",Str ":"] +,Para [Str "Here\8217s",Space,Str "another:"] ,OrderedList (1,Decimal,Period) [[Para [Str "First"]] - ,[Para [Str "Second",Str ":"] + ,[Para [Str "Second:"] ,BlockQuote [BulletList [[Plain [Str "Fee"]] ,[Plain [Str "Fie"]] ,[Plain [Str "Foe"]]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,Header 2 ("",[],[]) [Str "Fancy",Space,Str "list",Space,Str "markers"] ,OrderedList (2,Decimal,TwoParens) [[Plain [Str "begins",Space,Str "with",Space,Str "2"]] ,[Para [Str "and",Space,Str "now",Space,Str "3"] @@ -129,111 +129,112 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ": ,OrderedList (1,UpperAlpha,TwoParens) [[Plain [Str "a",Space,Str "subsublist"]] ,[Plain [Str "a",Space,Str "subsublist"]]]]]]] -,Para [Str "Nesting",Str ":"] +,Para [Str "Nesting:"] ,OrderedList (1,UpperAlpha,Period) [[Para [Str "Upper",Space,Str "Alpha"] ,OrderedList (1,UpperRoman,Period) - [[Para [Str "Upper",Space,Str "Roman",Str "."] + [[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) [[Plain [Str "Lower",Space,Str "alpha",Space,Str "with",Space,Str "paren"]]]]]]]]] -,Para [Str "Autonumbering",Str ":"] +,Para [Str "Autonumbering:"] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Autonumber",Str "."]] - ,[Para [Str "More",Str "."] + [[Plain [Str "Autonumber."]] + ,[Para [Str "More."] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Nested",Str "."]]]]] -,Para [Str "Autonumbering",Space,Str "with",Space,Str "explicit",Space,Str "start",Str ":"] + [[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 [Str "Definition"] +,Header 2 ("",[],[]) [Str "Definition"] ,DefinitionList [([Str "term",Space,Str "1"], - [[Para [Str "Definition",Space,Str "1",Str "."]]]) + [[Para [Str "Definition",Space,Str "1."]]]) ,([Str "term",Space,Str "2"], - [[Para [Str "Definition",Space,Str "2,",Space,Str "paragraph",Space,Str "1",Str "."] - ,Para [Str "Definition",Space,Str "2,",Space,Str "paragraph",Space,Str "2",Str "."]]]) + [[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",Str "."]]])] -,Header 1 [Str "Field",Space,Str "Lists"] + [[Para [Str "Definition",Space,Str "3."]]])] +,Header 1 ("",[],[]) [Str "Field",Space,Str "Lists"] ,BlockQuote [DefinitionList [([Str "address"], - [[Para [Str "61",Space,Str "Main",Space,Str "St",Str "."]]]) + [[Para [Str "61",Space,Str "Main",Space,Str "St."]]]) ,([Str "city"], [[Para [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]]) ,([Str "phone"], - [[Para [Str "123",Str "-",Str "4567"]]])]] + [[Para [Str "123-4567"]]])]] ,DefinitionList [([Str "address"], - [[Para [Str "61",Space,Str "Main",Space,Str "St",Str "."]]]) + [[Para [Str "61",Space,Str "Main",Space,Str "St."]]]) ,([Str "city"], [[Para [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]]) ,([Str "phone"], - [[Para [Str "123",Str "-",Str "4567"]]])] -,Header 1 [Str "HTML",Space,Str "Blocks"] -,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line",Str ":"] -,RawBlock "html" "<div>foo</div>\n" -,Para [Str "Now,",Space,Str "nested",Str ":"] -,RawBlock "html" "<div>\n <div>\n <div>\n foo\n </div>\n </div>\n</div>\n" -,Header 1 [Str "LaTeX",Space,Str "Block"] -,RawBlock "latex" "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}\n" -,Header 1 [Str "Inline",Space,Str "Markup"] + [[Para [Str "123-4567"]]])] +,Header 1 ("",[],[]) [Str "HTML",Space,Str "Blocks"] +,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line:"] +,RawBlock "html" "<div>foo</div>" +,Para [Str "Now,",Space,Str "nested:"] +,RawBlock "html" "<div>\n <div>\n <div>\n foo\n </div>\n </div>\n</div>" +,Header 1 ("",[],[]) [Str "LaTeX",Space,Str "Block"] +,RawBlock "latex" "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" +,Header 1 ("",[],[]) [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",Str ":",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",Space,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",Space,Code ("",[],[]) "<html>",Str "."] +,Para [Str "This",Space,Str "is",Space,Str "code:",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",Space,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",Space,Code ("",[],[]) "<html>",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 [Str "Special",Space,Str "Characters"] -,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "unicode",Str ":"] +,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",Str ":",Space,Str "\206"]] - ,[Plain [Str "o",Space,Str "umlaut",Str ":",Space,Str "\246"]] - ,[Plain [Str "section",Str ":",Space,Str "\167"]] - ,[Plain [Str "set",Space,Str "membership",Str ":",Space,Str "\8712"]] - ,[Plain [Str "copyright",Str ":",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",Str "."] -,Para [Str "This",Space,Str "&",Space,Str "that",Str "."] -,Para [Str "4",Space,Str "<",Space,Str "5",Str "."] -,Para [Str "6",Space,Str ">",Space,Str "5",Str "."] -,Para [Str "Backslash",Str ":",Space,Str "\\"] -,Para [Str "Backtick",Str ":",Space,Str "`"] -,Para [Str "Asterisk",Str ":",Space,Str "*"] -,Para [Str "Underscore",Str ":",Space,Str "_"] -,Para [Str "Left",Space,Str "brace",Str ":",Space,Str "{"] -,Para [Str "Right",Space,Str "brace",Str ":",Space,Str "}"] -,Para [Str "Left",Space,Str "bracket",Str ":",Space,Str "["] -,Para [Str "Right",Space,Str "bracket",Str ":",Space,Str "]"] -,Para [Str "Left",Space,Str "paren",Str ":",Space,Str "("] -,Para [Str "Right",Space,Str "paren",Str ":",Space,Str ")"] -,Para [Str "Greater",Str "-",Str "than",Str ":",Space,Str ">"] -,Para [Str "Hash",Str ":",Space,Str "#"] -,Para [Str "Period",Str ":",Space,Str "."] -,Para [Str "Bang",Str ":",Space,Str "!"] -,Para [Str "Plus",Str ":",Space,Str "+"] -,Para [Str "Minus",Str ":",Space,Str "-"] -,Header 1 [Str "Links"] -,Para [Str "Explicit",Str ":",Space,Str "a",Space,Link [Str "URL"] ("/url/",""),Str "."] -,Para [Str "Two",Space,Str "anonymous",Space,Str "links",Str ":",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",Str ":",Space,Link [Str "link1"] ("/url1/",""),Space,Str "and",Space,Link [Str "link2"] ("/url2/",""),Space,Str "and",Space,Link [Str "link1"] ("/url1/",""),Space,Str "again",Str "."] -,Para [Str "Here",Str "\8217",Str "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",Str "\8217",Str "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",Str ":",Space,Link [Str "AT&T"] ("/url/",""),Str "."] -,Para [Str "Autolinks",Str ":",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",Str ":"] + [[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 ("",[],[]) [Str "Links"] +,Para [Str "Explicit:",Space,Str "a",Space,Link [Str "URL"] ("/url/",""),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 "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 [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 "(",Str "1902",Str ")",Str ":"] -,Plain [Image [Str "image"] ("lalune.jpg","")] -,Plain [Image [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",Str "."] -,Header 1 [Str "Comments"] +,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):"] +,Para [Image [Str "image"] ("lalune.jpg","")] +,Para [Image [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 ("",[],[]) [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 [Str "Line",Space,Str "blocks"] -,Para [Str "But",Space,Str "can",Space,Str "a",Space,Str "bee",Space,Str "be",Space,Str "said",Space,Str "to",Space,Str "be",LineBreak,Str " ",Str "or",Space,Str "not",Space,Str "to",Space,Str "be",Space,Str "an",Space,Str "entire",Space,Str "bee,",LineBreak,Str " ",Str "when",Space,Str "half",Space,Str "the",Space,Str "bee",Space,Str "is",Space,Str "not",Space,Str "a",Space,Str "bee,",LineBreak,Str " ",Str "due",Space,Str "to",Space,Str "some",Space,Str "ancient",Space,Str "injury?"] -,Para [Str "Continuation",Space,Str "line",LineBreak,Str " ",Str "and",Space,Str "another"] -,Header 1 [Str "Simple",Space,Str "Tables"] +,Header 1 ("",[],[]) [Str "Line",Space,Str "blocks"] +,Para [Str "But",Space,Str "can",Space,Str "a",Space,Str "bee",Space,Str "be",Space,Str "said",Space,Str "to",Space,Str "be",LineBreak,Str "\160\160\160\160or",Space,Str "not",Space,Str "to",Space,Str "be",Space,Str "an",Space,Str "entire",Space,Str "bee,",LineBreak,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,",LineBreak,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?"] +,Para [Str "Continuation",Space,Str "line",LineBreak,Str "\160\160and",Space,Str "another"] +,Header 1 ("",[],[]) [Str "Simple",Space,Str "Tables"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] [[Plain [Str "col",Space,Str "1"]] ,[Plain [Str "col",Space,Str "2"]] @@ -255,7 +256,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ": ,[[Plain [Str "r2",Space,Str "d"]] ,[Plain [Str "e"]] ,[Plain [Str "f"]]]] -,Header 1 [Str "Grid",Space,Str "Tables"] +,Header 1 ("",[],[]) [Str "Grid",Space,Str "Tables"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.2375,0.15,0.1625] [[Plain [Str "col",Space,Str "1"]] ,[Plain [Str "col",Space,Str "2"]] @@ -300,24 +301,26 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ": ,[Plain [Str "b",Space,Str "2"]] ,[Plain [Str "b",Space,Str "2"]]]] ,[Plain [Str "c",Space,Str "c",Space,Str "2",Space,Str "c",Space,Str "2"]]]] -,Header 1 [Str "Footnotes"] -,Para [Note [Para [Str "Note",Space,Str "with",Space,Str "one",Space,Str "line",Str "."]]] -,Para [Note [Para [Str "Note",Space,Str "with",Space,Str "continuation",Space,Str "line",Str "."]]] -,Para [Note [Para [Str "Note",Space,Str "with"],Para [Str "continuation",Space,Str "block",Str "."]]] -,Para [Note [Para [Str "Note",Space,Str "with",Space,Str "continuation",Space,Str "line"],Para [Str "and",Space,Str "a",Space,Str "second",Space,Str "para",Str "."]]] -,Para [Str "Not",Space,Str "in",Space,Str "note",Str "."] -,Header 1 [Str "Math"] -,Para [Str "Some",Space,Str "inline",Space,Str "math",Space,Math InlineMath "E=mc^2",Str ".",Space,Str "Now",Space,Str "some",Space,Str "display",Space,Str "math",Str ":"] +,Header 1 ("",[],[]) [Str "Footnotes"] +,Para [Note [Para [Str "Note",Space,Str "with",Space,Str "one",Space,Str "line."]]] +,Para [Note [Para [Str "Note",Space,Str "with",Space,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",Space,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 ("",[],[]) [Str "Math"] +,Para [Str "Some",Space,Str "inline",Space,Str "math",Space,Math InlineMath "E=mc^2",Str ".",Space,Str "Now",Space,Str "some",Space,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 [Math DisplayMath "E &= mc^2\\\\\nF &= \\pi E",Math DisplayMath "F &= \\gamma \\alpha^2"] -,Para [Str "All",Space,Str "done",Str "."] -,Header 1 [Str "Default",Str "-",Str "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",Str "."] -,Header 2 [Str "Doesn",Str "\8217",Str "t",Space,Str "Break",Space,Str "Title",Space,Str "Parsing"] -,Para [Str "Inline",Space,Str "math",Str ":",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 ".",Space,Str "Other",Space,Str "roles",Str ":",Space,Superscript [Str "super"],Str ",",Space,Subscript [Str "sub"],Str "."] +,Para [Str "All",Space,Str "done."] +,Header 1 ("",[],[]) [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 ("",[],[]) [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 ".",Space,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",Str "-",Str "role",Space,Str "to",Space,Str "the",Space,Str "default",Space,Str "default",Str "."] -,Para [Str "And",Space,Str "now",Space,Str "`",Str "some",Str "-",Str "invalid",Str "-",Str "string",Str "-",Str "3231231",Str "`",Space,Str "is",Space,Str "nonsense",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,Str "some-invalid-string-3231231",Space,Str "is",Space,Str "nonsense."] +,Header 2 ("",[],[]) [Str "Literal",Space,Str "symbols"] +,Para [Str "2*2",Space,Str "=",Space,Str "4*1"]] diff --git a/tests/rst-reader.rst b/tests/rst-reader.rst index abe6d4f69..970ab4d4a 100644 --- a/tests/rst-reader.rst +++ b/tests/rst-reader.rst @@ -279,7 +279,8 @@ Field Lists :address: 61 Main St. :city: *Nowhere*, MA, USA -:phone: 123-4567 +:phone: + 123-4567 HTML Blocks =========== @@ -415,6 +416,12 @@ 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 ======== @@ -447,7 +454,7 @@ Line blocks | or not to be an entire bee, | when half the bee is not a bee, | due to some ancient injury? - +| | Continuation line | and @@ -555,8 +562,8 @@ display math: \alpha = \beta .. math:: - :label hithere - :nowrap + :label: hithere + :nowrap: E &= mc^2\\ F &= \pi E @@ -593,3 +600,7 @@ Reset default-role to the default default. And now `some-invalid-string-3231231` is nonsense. +Literal symbols +--------------- + +2*2 = 4*1 diff --git a/tests/s5.basic.html b/tests/s5.basic.html index 6194c27a9..f4e93eb6d 100644 --- a/tests/s5.basic.html +++ b/tests/s5.basic.html @@ -8,6 +8,7 @@ <meta name="author" content="Jen Jones" /> <meta name="date" content="2006-07-15" /> <title>My S5 Document</title> + <style type="text/css">code{white-space: pre;}</style> <!-- configuration parameters --> <meta name="defaultView" content="slideshow" /> <meta name="controlVis" content="hidden" /> diff --git a/tests/s5.fancy.html b/tests/s5.fancy.html index 119306143..c62fcb3db 100644 --- a/tests/s5.fancy.html +++ b/tests/s5.fancy.html @@ -8,6 +8,7 @@ <meta name="author" content="Jen Jones" /> <meta name="date" content="2006-07-15" /> <title>My S5 Document</title> + <style type="text/css">code{white-space: pre;}</style> <!-- configuration parameters --> <meta name="defaultView" content="slideshow" /> <meta name="controlVis" content="hidden" /> diff --git a/tests/s5.inserts.html b/tests/s5.inserts.html index 524c5b0ce..455225f9b 100644 --- a/tests/s5.inserts.html +++ b/tests/s5.inserts.html @@ -8,6 +8,7 @@ <meta name="author" content="Jen Jones" /> <meta name="date" content="2006-07-15" /> <title>My S5 Document</title> + <style type="text/css">code{white-space: pre;}</style> <link rel="stylesheet" href="main.css" type="text/css" /> STUFF INSERTED </head> diff --git a/tests/s5.native b/tests/s5.native index 020ee4079..3bf512787 100644 --- a/tests/s5.native +++ b/tests/s5.native @@ -1,8 +1,8 @@ Pandoc (Meta {docTitle = [Str "My",Space,Str "S5",Space,Str "Document"], docAuthors = [[Str "Sam",Space,Str "Smith"],[Str "Jen",Space,Str "Jones"]], docDate = [Str "July",Space,Str "15,",Space,Str "2006"]}) -[Header 1 [Str "First",Space,Str "slide"] +[Header 1 ("first-slide",[],[]) [Str "First",Space,Str "slide"] ,BulletList [[Plain [Str "first",Space,Str "bullet"]] ,[Plain [Str "second",Space,Str "bullet"]]] -,Header 1 [Str "Math"] +,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}"]]]] diff --git a/tests/tables-rstsubset.native b/tests/tables-rstsubset.native index ef89fea29..887d0ca17 100644 --- a/tests/tables-rstsubset.native +++ b/tests/tables-rstsubset.native @@ -1,4 +1,4 @@ -[Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption",Str ":"] +[Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.125,0.1125,0.1375,0.15] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] @@ -16,8 +16,8 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Table",Str ":",Space,Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax",Str "."] -,Para [Str "Simple",Space,Str "table",Space,Str "without",Space,Str "caption",Str ":"] +,Para [Str "Table:",Space,Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] +,Para [Str "Simple",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.125,0.1125,0.1375,0.15] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] @@ -35,7 +35,7 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Simple",Space,Str "table",Space,Str "indented",Space,Str "two",Space,Str "spaces",Str ":"] +,Para [Str "Simple",Space,Str "table",Space,Str "indented",Space,Str "two",Space,Str "spaces:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.125,0.1125,0.1375,0.15] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] @@ -53,8 +53,8 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Table",Str ":",Space,Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax",Str "."] -,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption",Str ":"] +,Para [Str "Table:",Space,Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] +,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.175,0.1625,0.1875,0.3625] [[Plain [Str "Centered",Space,Str "Header"]] ,[Plain [Str "Left",Space,Str "Aligned"]] @@ -62,14 +62,14 @@ ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]] -,Para [Str "Table",Str ":",Space,Str "Here",Str "'",Str "s",Space,Str "the",Space,Str "caption",Str ".",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines",Str "."] -,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption",Str ":"] + ,[Plain [Str "5.0"]] + ,[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."]]]] +,Para [Str "Table:",Space,Str "Here's",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] +,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.175,0.1625,0.1875,0.3625] [[Plain [Str "Centered",Space,Str "Header"]] ,[Plain [Str "Left",Space,Str "Aligned"]] @@ -77,13 +77,13 @@ ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]] -,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers",Str ":"] + ,[Plain [Str "5.0"]] + ,[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."]]]] +,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.1,0.1,0.1,0.1] [[] ,[] @@ -101,7 +101,7 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "column",Space,Str "headers",Str ":"] +,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.175,0.1625,0.1875,0.3625] [[] ,[] @@ -109,9 +109,9 @@ ,[]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]]] + ,[Plain [Str "5.0"]] + ,[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."]]]]] diff --git a/tests/tables.fb2 b/tests/tables.fb2 new file mode 100644 index 000000000..f636e9fd4 --- /dev/null +++ b/tests/tables.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p>Simple table with caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Simple table without caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis /></p><p>Simple table indented two spaces:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Multiline table with caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here's another one. Note the blank line between rows.</td></tr></table><p><emphasis>Here's the caption. It may span multiple lines.</emphasis></p><p>Multiline table without caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here's another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p><p>Table without column headers:</p><table><tr><th align="right" /><th align="left" /><th align="center" /><th align="right" /></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="right">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="right">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="right">1</td></tr></table><p><emphasis /></p><p>Multiline table without column headers:</p><table><tr><th align="center" /><th align="left" /><th align="right" /><th align="left" /></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here's another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p></section></body></FictionBook>
\ No newline at end of file diff --git a/tests/tables.latex b/tests/tables.latex index 56b469a54..78d53a998 100644 --- a/tests/tables.latex +++ b/tests/tables.latex @@ -1,175 +1,169 @@ Simple table with caption: -\ctable[caption = {Demonstration of simple table syntax.}, -pos = H, center, botcap]{rlcl} -{% notes -} -{% rows -\FL +\begin{longtable}[c]{rlcl} +\hline\noalign{\medskip} Right & Left & Center & Default -\ML +\\\noalign{\medskip} +\hline\noalign{\medskip} 12 & 12 & 12 & 12 \\\noalign{\medskip} 123 & 123 & 123 & 123 \\\noalign{\medskip} 1 & 1 & 1 & 1 -\LL -} +\\\noalign{\medskip} +\hline +\noalign{\medskip} +\caption{Demonstration of simple table syntax.} +\end{longtable} Simple table without caption: -\ctable[pos = H, center, botcap]{rlcl} -{% notes -} -{% rows -\FL +\begin{longtable}[c]{rlcl} +\hline\noalign{\medskip} Right & Left & Center & Default -\ML +\\\noalign{\medskip} +\hline\noalign{\medskip} 12 & 12 & 12 & 12 \\\noalign{\medskip} 123 & 123 & 123 & 123 \\\noalign{\medskip} 1 & 1 & 1 & 1 -\LL -} +\\\noalign{\medskip} +\hline +\end{longtable} Simple table indented two spaces: -\ctable[caption = {Demonstration of simple table syntax.}, -pos = H, center, botcap]{rlcl} -{% notes -} -{% rows -\FL +\begin{longtable}[c]{rlcl} +\hline\noalign{\medskip} Right & Left & Center & Default -\ML +\\\noalign{\medskip} +\hline\noalign{\medskip} 12 & 12 & 12 & 12 \\\noalign{\medskip} 123 & 123 & 123 & 123 \\\noalign{\medskip} 1 & 1 & 1 & 1 -\LL -} +\\\noalign{\medskip} +\hline +\noalign{\medskip} +\caption{Demonstration of simple table syntax.} +\end{longtable} Multiline table with caption: -\ctable[caption = {Here's the caption. It may span multiple lines.}, -pos = H, center, botcap]{clrl} -{% notes -} -{% rows -\FL -\parbox[b]{0.15\columnwidth}{\centering +\begin{longtable}[c]{clrl} +\hline\noalign{\medskip} +\begin{minipage}[b]{0.15\columnwidth}\centering Centered Header -} & \parbox[b]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[b]{0.14\columnwidth}\raggedright Left Aligned -} & \parbox[b]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[b]{0.16\columnwidth}\raggedleft Right Aligned -} & \parbox[b]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[b]{0.34\columnwidth}\raggedright Default aligned -} -\ML -\parbox[t]{0.15\columnwidth}{\centering +\end{minipage} +\\\noalign{\medskip} +\hline\noalign{\medskip} +\begin{minipage}[t]{0.15\columnwidth}\centering First -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 12.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Example of a row that spans multiple lines. -} +\end{minipage} \\\noalign{\medskip} -\parbox[t]{0.15\columnwidth}{\centering +\begin{minipage}[t]{0.15\columnwidth}\centering Second -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 5.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Here's another one. Note the blank line between rows. -} -\LL -} +\end{minipage} +\\\noalign{\medskip} +\hline +\noalign{\medskip} +\caption{Here's the caption. It may span multiple lines.} +\end{longtable} Multiline table without caption: -\ctable[pos = H, center, botcap]{clrl} -{% notes -} -{% rows -\FL -\parbox[b]{0.15\columnwidth}{\centering +\begin{longtable}[c]{clrl} +\hline\noalign{\medskip} +\begin{minipage}[b]{0.15\columnwidth}\centering Centered Header -} & \parbox[b]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[b]{0.14\columnwidth}\raggedright Left Aligned -} & \parbox[b]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[b]{0.16\columnwidth}\raggedleft Right Aligned -} & \parbox[b]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[b]{0.34\columnwidth}\raggedright Default aligned -} -\ML -\parbox[t]{0.15\columnwidth}{\centering +\end{minipage} +\\\noalign{\medskip} +\hline\noalign{\medskip} +\begin{minipage}[t]{0.15\columnwidth}\centering First -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 12.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Example of a row that spans multiple lines. -} +\end{minipage} \\\noalign{\medskip} -\parbox[t]{0.15\columnwidth}{\centering +\begin{minipage}[t]{0.15\columnwidth}\centering Second -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 5.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Here's another one. Note the blank line between rows. -} -\LL -} +\end{minipage} +\\\noalign{\medskip} +\hline +\end{longtable} Table without column headers: -\ctable[pos = H, center, botcap]{rlcr} -{% notes -} -{% rows -\FL +\begin{longtable}[c]{rlcr} +\hline\noalign{\medskip} 12 & 12 & 12 & 12 \\\noalign{\medskip} 123 & 123 & 123 & 123 \\\noalign{\medskip} 1 & 1 & 1 & 1 -\LL -} +\\\noalign{\medskip} +\hline +\end{longtable} Multiline table without column headers: -\ctable[pos = H, center, botcap]{clrl} -{% notes -} -{% rows -\FL -\parbox[t]{0.15\columnwidth}{\centering +\begin{longtable}[c]{clrl} +\hline\noalign{\medskip} +\begin{minipage}[t]{0.15\columnwidth}\centering First -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 12.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Example of a row that spans multiple lines. -} +\end{minipage} \\\noalign{\medskip} -\parbox[t]{0.15\columnwidth}{\centering +\begin{minipage}[t]{0.15\columnwidth}\centering Second -} & \parbox[t]{0.14\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.14\columnwidth}\raggedright row -} & \parbox[t]{0.16\columnwidth}{\raggedleft +\end{minipage} & \begin{minipage}[t]{0.16\columnwidth}\raggedleft 5.0 -} & \parbox[t]{0.34\columnwidth}{\raggedright +\end{minipage} & \begin{minipage}[t]{0.34\columnwidth}\raggedright Here's another one. Note the blank line between rows. -} -\LL -} +\end{minipage} +\\\noalign{\medskip} +\hline +\end{longtable} diff --git a/tests/tables.native b/tests/tables.native index 1d714d730..00a7c5970 100644 --- a/tests/tables.native +++ b/tests/tables.native @@ -1,5 +1,5 @@ -[Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption",Str ":"] -,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax",Str "."] [AlignRight,AlignLeft,AlignCenter,AlignDefault] [0.0,0.0,0.0,0.0] +[Para [Str "Simple",Space,Str "table",Space,Str "with",Space,Str "caption:"] +,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] [AlignRight,AlignLeft,AlignCenter,AlignDefault] [0.0,0.0,0.0,0.0] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] ,[Plain [Str "Center"]] @@ -16,7 +16,7 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Simple",Space,Str "table",Space,Str "without",Space,Str "caption",Str ":"] +,Para [Str "Simple",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignRight,AlignLeft,AlignCenter,AlignDefault] [0.0,0.0,0.0,0.0] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] @@ -34,8 +34,8 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Simple",Space,Str "table",Space,Str "indented",Space,Str "two",Space,Str "spaces",Str ":"] -,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax",Str "."] [AlignRight,AlignLeft,AlignCenter,AlignDefault] [0.0,0.0,0.0,0.0] +,Para [Str "Simple",Space,Str "table",Space,Str "indented",Space,Str "two",Space,Str "spaces:"] +,Table [Str "Demonstration",Space,Str "of",Space,Str "simple",Space,Str "table",Space,Str "syntax."] [AlignRight,AlignLeft,AlignCenter,AlignDefault] [0.0,0.0,0.0,0.0] [[Plain [Str "Right"]] ,[Plain [Str "Left"]] ,[Plain [Str "Center"]] @@ -52,21 +52,21 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption",Str ":"] -,Table [Str "Here",Str "'",Str "s",Space,Str "the",Space,Str "caption",Str ".",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines",Str "."] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] +,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption:"] +,Table [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,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] [[Plain [Str "Centered",Space,Str "Header"]] ,[Plain [Str "Left",Space,Str "Aligned"]] ,[Plain [Str "Right",Space,Str "Aligned"]] ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]] -,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption",Str ":"] + ,[Plain [Str "5.0"]] + ,[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."]]]] +,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] [[Plain [Str "Centered",Space,Str "Header"]] ,[Plain [Str "Left",Space,Str "Aligned"]] @@ -74,13 +74,13 @@ ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]] -,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers",Str ":"] + ,[Plain [Str "5.0"]] + ,[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."]]]] +,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignRight,AlignLeft,AlignCenter,AlignRight] [0.0,0.0,0.0,0.0] [[] ,[] @@ -98,7 +98,7 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] -,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "column",Space,Str "headers",Str ":"] +,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignCenter,AlignLeft,AlignRight,AlignDefault] [0.15,0.1375,0.1625,0.3375] [[] ,[] @@ -106,9 +106,9 @@ ,[]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] - ,[Plain [Str "12",Str ".",Str "0"]] - ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines",Str "."]]] + ,[Plain [Str "12.0"]] + ,[Plain [Str "Example",Space,Str "of",Space,Str "a",Space,Str "row",Space,Str "that",Space,Str "spans",Space,Str "multiple",Space,Str "lines."]]] ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] - ,[Plain [Str "5",Str ".",Str "0"]] - ,[Plain [Str "Here",Str "'",Str "s",Space,Str "another",Space,Str "one",Str ".",Space,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows",Str "."]]]]] + ,[Plain [Str "5.0"]] + ,[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."]]]]] diff --git a/tests/tables.rtf b/tests/tables.rtf index e7c1e9f60..011724967 100644 --- a/tests/tables.rtf +++ b/tests/tables.rtf @@ -357,4 +357,3 @@ } \intbl\row} {\pard \ql \f0 \sa180 \li0 \fi0 \par} - diff --git a/tests/test-pandoc.hs b/tests/test-pandoc.hs index 968f31df6..24b7a8261 100644 --- a/tests/test-pandoc.hs +++ b/tests/test-pandoc.hs @@ -3,7 +3,7 @@ module Main where import Test.Framework - +import GHC.IO.Encoding import qualified Tests.Old import qualified Tests.Readers.LaTeX import qualified Tests.Readers.Markdown @@ -34,4 +34,6 @@ tests = [ testGroup "Old" Tests.Old.tests ] main :: IO () -main = inDirectory "tests" $ defaultMain tests +main = do + setLocaleEncoding utf8 + inDirectory "tests" $ defaultMain tests diff --git a/tests/testsuite.native b/tests/testsuite.native index 691c4959a..90727a660 100644 --- a/tests/testsuite.native +++ b/tests/testsuite.native @@ -1,172 +1,172 @@ -Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [Str "July",Space,Str "17",Str ",",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",Str ".",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",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."] +Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [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",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,HorizontalRule -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] -,Header 3 [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 1 [Str "Level",Space,Str "1"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 3 [Str "Level",Space,Str "3"] +,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 [Str "Level",Space,Str "2"] +,Header 2 ("level-2",[],[]) [Str "Level",Space,Str "2"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] ,HorizontalRule -,Header 1 [Str "Paragraphs"] -,Para [Str "Here\8217s",Space,Str "a",Space,Str "regular",Space,Str "paragraph",Str "."] -,Para [Str "In",Space,Str "Markdown",Space,Str "1",Str ".",Str "0",Str ".",Str "0",Space,Str "and",Space,Str "earlier",Str ".",Space,Str "Version",Space,Str "8",Str ".",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item",Str ".",Space,Str "Because",Space,Str "a",Space,Str "hard",Str "-",Str "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",Str "."] -,Para [Str "Here\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet",Str ".",Space,Str "*",Space,Str "criminey",Str "."] -,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "hard",Space,Str "line",Space,Str "break",LineBreak,Str "here",Str "."] +,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",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\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",Str "-",Str "mail",Space,Str "style",Str ":"] +,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",Str ".",Space,Str "It",Space,Str "is",Space,Str "pretty",Space,Str "short",Str "."]] + [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",Str ":"] + [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",Str ":"] + ,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",Str ":"] + ,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",Str ":",Space,Str "2",Space,Str ">",Space,Str "1",Str "."] -,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph",Str "."] +,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 -,Header 1 [Str "Code",Space,Str "Blocks"] -,Para [Str "Code",Str ":"] +,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",Str ":"] +,Para [Str "And:"] ,CodeBlock ("",[],[]) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" ,HorizontalRule -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] -,Para [Str "Asterisks",Space,Str "tight",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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 [Str "Ordered"] -,Para [Str "Tight",Str ":"] +,Header 2 ("ordered",[],[]) [Str "Ordered"] +,Para [Str "Tight:"] ,OrderedList (1,Decimal,Period) [[Plain [Str "First"]] ,[Plain [Str "Second"]] ,[Plain [Str "Third"]]] -,Para [Str "and",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,Para [Str "Multiple",Space,Str "paragraphs:"] ,OrderedList (1,Decimal,Period) - [[Para [Str "Item",Space,Str "1",Str ",",Space,Str "graf",Space,Str "one",Str "."] - ,Para [Str "Item",Space,Str "1",Str ".",Space,Str "graf",Space,Str "two",Str ".",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",Str "."]] - ,[Para [Str "Item",Space,Str "2",Str "."]] - ,[Para [Str "Item",Space,Str "3",Str "."]]] -,Header 2 [Str "Nested"] + [[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."]]] +,Header 2 ("nested",[],[]) [Str "Nested"] ,BulletList [[Plain [Str "Tab"] ,BulletList [[Plain [Str "Tab"] ,BulletList [[Plain [Str "Tab"]]]]]]] -,Para [Str "Here\8217s",Space,Str "another",Str ":"] +,Para [Str "Here\8217s",Space,Str "another:"] ,OrderedList (1,Decimal,Period) [[Plain [Str "First"]] - ,[Plain [Str "Second",Str ":"] + ,[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",Str ":"] +,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",Str ":"] + ,[Para [Str "Second:"] ,BulletList [[Plain [Str "Fee"]] ,[Plain [Str "Fie"]] ,[Plain [Str "Foe"]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Tabs",Space,Str "and",Space,Str "spaces"] +,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 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,Header 2 ("fancy-list-markers",[],[]) [Str "Fancy",Space,Str "list",Space,Str "markers"] ,OrderedList (2,Decimal,TwoParens) [[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,Period) - [[Plain [Str "sublist",Space,Str "with",Space,Str "roman",Space,Str "numerals",Str ",",Space,Str "starting",Space,Str "with",Space,Str "4"]] + [[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",Str ":"] +,Para [Str "Nesting:"] ,OrderedList (1,UpperAlpha,Period) [[Plain [Str "Upper",Space,Str "Alpha"] ,OrderedList (1,UpperRoman,Period) - [[Plain [Str "Upper",Space,Str "Roman",Str "."] + [[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",Str ":"] +,Para [Str "Autonumbering:"] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Autonumber",Str "."]] - ,[Plain [Str "More",Str "."] + [[Plain [Str "Autonumber."]] + ,[Plain [Str "More."] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Nested",Str "."]]]]] -,Para [Str "Should",Space,Str "not",Space,Str "be",Space,Str "a",Space,Str "list",Space,Str "item",Str ":"] -,Para [Str "M.A.\160",Str "2007"] -,Para [Str "B",Str ".",Space,Str "Williams"] + [[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 [Str "Definition",Space,Str "Lists"] -,Para [Str "Tight",Space,Str "using",Space,Str "spaces",Str ":"] +,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"]]]) @@ -174,7 +174,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Plain [Str "orange",Space,Str "fruit"]]]) ,([Str "banana"], [[Plain [Str "yellow",Space,Str "fruit"]]])] -,Para [Str "Tight",Space,Str "using",Space,Str "tabs",Str ":"] +,Para [Str "Tight",Space,Str "using",Space,Str "tabs:"] ,DefinitionList [([Str "apple"], [[Plain [Str "red",Space,Str "fruit"]]]) @@ -182,7 +182,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Plain [Str "orange",Space,Str "fruit"]]]) ,([Str "banana"], [[Plain [Str "yellow",Space,Str "fruit"]]])] -,Para [Str "Loose",Str ":"] +,Para [Str "Loose:"] ,DefinitionList [([Str "apple"], [[Para [Str "red",Space,Str "fruit"]]]) @@ -190,17 +190,17 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[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",Str ":"] +,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",Str ",",Space,Str "crisp",Str ",",Space,Str "pleasant",Space,Str "to",Space,Str "taste"]]]) + ,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",Str ",",Space,Str "tight",Str ":"] +,Para [Str "Multiple",Space,Str "definitions,",Space,Str "tight:"] ,DefinitionList [([Str "apple"], [[Plain [Str "red",Space,Str "fruit"]] @@ -208,7 +208,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,([Str "orange"], [[Plain [Str "orange",Space,Str "fruit"]] ,[Plain [Str "bank"]]])] -,Para [Str "Multiple",Space,Str "definitions",Str ",",Space,Str "loose",Str ":"] +,Para [Str "Multiple",Space,Str "definitions,",Space,Str "loose:"] ,DefinitionList [([Str "apple"], [[Para [Str "red",Space,Str "fruit"]] @@ -216,7 +216,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,([Str "orange"], [[Para [Str "orange",Space,Str "fruit"]] ,[Para [Str "bank"]]])] -,Para [Str "Blank",Space,Str "line",Space,Str "after",Space,Str "term",Str ",",Space,Str "indented",Space,Str "marker",Str ",",Space,Str "alternate",Space,Str "markers",Str ":"] +,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"]] @@ -226,171 +226,171 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,OrderedList (1,Decimal,Period) [[Plain [Str "sublist"]] ,[Plain [Str "sublist"]]]]])] -,Header 1 [Str "HTML",Space,Str "Blocks"] -,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line",Str ":"] +,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 "html" "<div>" ,Plain [Str "foo"] ,RawBlock "html" "</div>\n" -,Para [Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation",Str ":"] +,Para [Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation:"] ,RawBlock "html" "<div>\n<div>\n<div>" ,Plain [Str "foo"] ,RawBlock "html" "</div>\n</div>\n<div>" ,Plain [Str "bar"] ,RawBlock "html" "</div>\n</div>\n" -,Para [Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table",Str ":"] +,Para [Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table:"] ,RawBlock "html" "<table>\n<tr>\n<td>" ,Plain [Str "This",Space,Str "is",Space,Emph [Str "emphasized"]] ,RawBlock "html" "</td>\n<td>" ,Plain [Str "And",Space,Str "this",Space,Str "is",Space,Strong [Str "strong"]] ,RawBlock "html" "</td>\n</tr>\n</table>\n\n<script type=\"text/javascript\">document.write('This *should not* be interpreted as markdown');</script>\n" -,Para [Str "Here\8217s",Space,Str "a",Space,Str "simple",Space,Str "block",Str ":"] +,Para [Str "Here\8217s",Space,Str "a",Space,Str "simple",Space,Str "block:"] ,RawBlock "html" "<div>\n " ,Plain [Str "foo"] ,RawBlock "html" "</div>\n" -,Para [Str "This",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "code",Space,Str "block",Str ",",Space,Str "though",Str ":"] +,Para [Str "This",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "code",Space,Str "block,",Space,Str "though:"] ,CodeBlock ("",[],[]) "<div>\n foo\n</div>" -,Para [Str "As",Space,Str "should",Space,Str "this",Str ":"] +,Para [Str "As",Space,Str "should",Space,Str "this:"] ,CodeBlock ("",[],[]) "<div>foo</div>" -,Para [Str "Now",Str ",",Space,Str "nested",Str ":"] +,Para [Str "Now,",Space,Str "nested:"] ,RawBlock "html" "<div>\n <div>\n <div>\n " ,Plain [Str "foo"] ,RawBlock "html" "</div>\n </div>\n</div>\n" -,Para [Str "This",Space,Str "should",Space,Str "just",Space,Str "be",Space,Str "an",Space,Str "HTML",Space,Str "comment",Str ":"] +,Para [Str "This",Space,Str "should",Space,Str "just",Space,Str "be",Space,Str "an",Space,Str "HTML",Space,Str "comment:"] ,RawBlock "html" "<!-- Comment -->\n" -,Para [Str "Multiline",Str ":"] +,Para [Str "Multiline:"] ,RawBlock "html" "<!--\nBlah\nBlah\n-->\n\n<!--\n This is another comment.\n-->\n" -,Para [Str "Code",Space,Str "block",Str ":"] +,Para [Str "Code",Space,Str "block:"] ,CodeBlock ("",[],[]) "<!-- Comment -->" -,Para [Str "Just",Space,Str "plain",Space,Str "comment",Str ",",Space,Str "with",Space,Str "trailing",Space,Str "spaces",Space,Str "on",Space,Str "the",Space,Str "line",Str ":"] +,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 "html" "<!-- foo --> \n" -,Para [Str "Code",Str ":"] +,Para [Str "Code:"] ,CodeBlock ("",[],[]) "<hr />" -,Para [Str "Hr\8217s",Str ":"] +,Para [Str "Hr\8217s:"] ,RawBlock "html" "<hr>\n\n<hr />\n\n<hr />\n\n<hr> \n\n<hr /> \n\n<hr /> \n\n<hr class=\"foo\" id=\"bar\" />\n\n<hr class=\"foo\" id=\"bar\" />\n\n<hr class=\"foo\" id=\"bar\">\n" ,HorizontalRule -,Header 1 [Str "Inline",Space,Str "Markup"] +,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",Str "."]]] -,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Str "."] -,Para [Strong [Emph [Str "This",Space,Str "is",Space,Str "strong",Space,Str "and",Space,Str "em",Str "."]]] -,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Str "."] -,Para [Str "This",Space,Str "is",Space,Str "code",Str ":",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",Space,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",Space,Code ("",[],[]) "<html>",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 ("",[],[]) "<html>",Str "."] ,Para [Strikeout [Str "This",Space,Str "is",Space,Emph [Str "strikeout"],Str "."]] -,Para [Str "Superscripts",Str ":",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Superscript [Emph [Str "hello"]],Space,Str "a",Superscript [Str "hello",Str "\160",Str "there"],Str "."] -,Para [Str "Subscripts",Str ":",Space,Str "H",Subscript [Str "2"],Str "O",Str ",",Space,Str "H",Subscript [Str "23"],Str "O",Str ",",Space,Str "H",Subscript [Str "many",Str "\160",Str "of",Str "\160",Str "them"],Str "O",Str "."] -,Para [Str "These",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "superscripts",Space,Str "or",Space,Str "subscripts",Str ",",Space,Str "because",Space,Str "of",Space,Str "the",Space,Str "unescaped",Space,Str "spaces",Str ":",Space,Str "a",Str "^",Str "b",Space,Str "c",Str "^",Str "d",Str ",",Space,Str "a",Str "~",Str "b",Space,Str "c",Str "~",Str "d",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:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a~b",Space,Str "c~d."] ,HorizontalRule -,Header 1 [Str "Smart",Space,Str "quotes",Str ",",Space,Str "ellipses",Str ",",Space,Str "dashes"] -,Para [Quoted DoubleQuote [Str "Hello",Str ","],Space,Str "said",Space,Str "the",Space,Str "spider",Str ".",Space,Quoted DoubleQuote [Quoted SingleQuote [Str "Shelob"],Space,Str "is",Space,Str "my",Space,Str "name",Str "."]] -,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",Str "."] -,Para [Quoted SingleQuote [Str "Oak",Str ","],Space,Quoted SingleQuote [Str "elm",Str ","],Space,Str "and",Space,Quoted SingleQuote [Str "beech"],Space,Str "are",Space,Str "names",Space,Str "of",Space,Str "trees",Str ".",Space,Str "So",Space,Str "is",Space,Quoted SingleQuote [Str "pine",Str "."]] -,Para [Quoted SingleQuote [Str "He",Space,Str "said",Str ",",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go",Str "."]],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70\8217s",Str "?"] +,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.",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",Space,Str "link"] ("http://example.com/?foo=1&bar=2","")],Str "."] -,Para [Str "Some",Space,Str "dashes",Str ":",Space,Str "one",Str "\8212",Str "two",Space,Str "\8212",Space,Str "three",Str "\8212",Str "four",Space,Str "\8212",Space,Str "five",Str "."] -,Para [Str "Dashes",Space,Str "between",Space,Str "numbers",Str ":",Space,Str "5",Str "\8211",Str "7",Str ",",Space,Str "255",Str "\8211",Str "66",Str ",",Space,Str "1987",Str "\8211",Str "1999",Str "."] -,Para [Str "Ellipses",Str "\8230",Str "and",Str "\8230",Str "and",Str "\8230",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 [Str "LaTeX"] +,Header 1 ("latex",[],[]) [Str "LaTeX"] ,BulletList [[Plain [RawInline "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 "-",Str "Tree"]] - ,[Plain [Str "Here\8217s",Space,Str "some",Space,Str "display",Space,Str "math",Str ":",Space,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",Str ":",Space,Math InlineMath "\\alpha + \\omega \\times x^2",Str "."]]] -,Para [Str "These",Space,Str "shouldn\8217t",Space,Str "be",Space,Str "math",Str ":"] + ,[Plain [Math InlineMath "p",Str "-Tree"]] + ,[Plain [Str "Here\8217s",Space,Str "some",Space,Str "display",Space,Str "math:",Space,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",Str ",",Space,Str "write",Space,Code ("",[],[]) "$e = mc^2$",Str "."]] - ,[Plain [Str "$",Str "22",Str ",",Str "000",Space,Str "is",Space,Str "a",Space,Emph [Str "lot"],Space,Str "of",Space,Str "money",Str ".",Space,Str "So",Space,Str "is",Space,Str "$",Str "34",Str ",",Str "000",Str ".",Space,Str "(",Str "It",Space,Str "worked",Space,Str "if",Space,Quoted DoubleQuote [Str "lot"],Space,Str "is",Space,Str "emphasized",Str ".",Str ")"]] - ,[Plain [Str "Shoes",Space,Str "(",Str "$",Str "20",Str ")",Space,Str "and",Space,Str "socks",Space,Str "(",Str "$",Str "5",Str ")",Str "."]] - ,[Plain [Str "Escaped",Space,Code ("",[],[]) "$",Str ":",Space,Str "$",Str "73",Space,Emph [Str "this",Space,Str "should",Space,Str "be",Space,Str "emphasized"],Space,Str "23",Str "$",Str "."]]] -,Para [Str "Here\8217s",Space,Str "a",Space,Str "LaTeX",Space,Str "table",Str ":"] + [[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",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 "latex" "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" ,HorizontalRule -,Header 1 [Str "Special",Space,Str "Characters"] -,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "unicode",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",Str ":",Space,Str "\206"]] - ,[Plain [Str "o",Space,Str "umlaut",Str ":",Space,Str "\246"]] - ,[Plain [Str "section",Str ":",Space,Str "\167"]] - ,[Plain [Str "set",Space,Str "membership",Str ":",Space,Str "\8712"]] - ,[Plain [Str "copyright",Str ":",Space,Str "\169"]]] -,Para [Str "AT",Str "&",Str "T",Space,Str "has",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "their",Space,Str "name",Str "."] -,Para [Str "AT",Str "&",Str "T",Space,Str "is",Space,Str "another",Space,Str "way",Space,Str "to",Space,Str "write",Space,Str "it",Str "."] -,Para [Str "This",Space,Str "&",Space,Str "that",Str "."] -,Para [Str "4",Space,Str "<",Space,Str "5",Str "."] -,Para [Str "6",Space,Str ">",Space,Str "5",Str "."] -,Para [Str "Backslash",Str ":",Space,Str "\\"] -,Para [Str "Backtick",Str ":",Space,Str "`"] -,Para [Str "Asterisk",Str ":",Space,Str "*"] -,Para [Str "Underscore",Str ":",Space,Str "_"] -,Para [Str "Left",Space,Str "brace",Str ":",Space,Str "{"] -,Para [Str "Right",Space,Str "brace",Str ":",Space,Str "}"] -,Para [Str "Left",Space,Str "bracket",Str ":",Space,Str "["] -,Para [Str "Right",Space,Str "bracket",Str ":",Space,Str "]"] -,Para [Str "Left",Space,Str "paren",Str ":",Space,Str "("] -,Para [Str "Right",Space,Str "paren",Str ":",Space,Str ")"] -,Para [Str "Greater",Str "-",Str "than",Str ":",Space,Str ">"] -,Para [Str "Hash",Str ":",Space,Str "#"] -,Para [Str "Period",Str ":",Space,Str "."] -,Para [Str "Bang",Str ":",Space,Str "!"] -,Para [Str "Plus",Str ":",Space,Str "+"] -,Para [Str "Minus",Str ":",Space,Str "-"] + [[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 [Str "Links"] -,Header 2 [Str "Explicit"] +,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",Str "_",Str "underscore"] ("/url/with_underscore","")] +,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 [Str "Reference"] +,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 "[",Str "brackets",Str "]"] ("/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",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 "[",Str "not",Str "]",Str "[",Str "]",Space,Str "be",Space,Str "a",Space,Str "link",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 [Str "With",Space,Str "ampersands"] +,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",Str ":",Space,Link [Str "AT",Str "&",Str "T"] ("http://att.com/","AT&T"),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 [Str "Autolinks"] -,Para [Str "With",Space,Str "an",Space,Str "ampersand",Str ":",Space,Link [Code ("",["url"],[]) "http://example.com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")] +,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",Str "?"]] - ,[Plain [Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] - ,[Plain [Str "It",Space,Str "should",Str "."]]] -,Para [Str "An",Space,Str "e",Str "-",Str "mail",Space,Str "address",Str ":",Space,Link [Code ("",["url"],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] + [[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,Link [Str "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] ,BlockQuote - [Para [Str "Blockquoted",Str ":",Space,Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] -,Para [Str "Auto",Str "-",Str "links",Space,Str "should",Space,Str "not",Space,Str "occur",Space,Str "here",Str ":",Space,Code ("",[],[]) "<http://example.com/>"] + [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 ("",[],[]) "<http://example.com/>"] ,CodeBlock ("",[],[]) "or here: <http://example.com/>" ,HorizontalRule -,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 "(",Str "1902",Str ")",Str ":"] -,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",Str "."] +,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 "lalune"] ("lalune.jpg","fig: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 [Str "Footnotes"] -,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "footnote",Space,Str "reference",Str ",",Note [Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "footnote",Str ".",Space,Str "It",Space,Str "can",Space,Str "go",Space,Str "anywhere",Space,Str "after",Space,Str "the",Space,Str "footnote",Space,Str "reference",Str ".",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",Str "."]],Space,Str "and",Space,Str "another",Str ".",Note [Para [Str "Here\8217s",Space,Str "the",Space,Str "long",Space,Str "note",Str ".",Space,Str "This",Space,Str "one",Space,Str "contains",Space,Str "multiple",Space,Str "blocks",Str "."],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 "(",Str "as",Space,Str "with",Space,Str "list",Space,Str "items",Str ")",Str "."],CodeBlock ("",[],[]) " { <code> }",Para [Str "If",Space,Str "you",Space,Str "want",Str ",",Space,Str "you",Space,Str "can",Space,Str "indent",Space,Str "every",Space,Str "line",Str ",",Space,Str "but",Space,Str "you",Space,Str "can",Space,Str "also",Space,Str "be",Space,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",Str "."]],Space,Str "This",Space,Str "should",Space,Emph [Str "not"],Space,Str "be",Space,Str "a",Space,Str "footnote",Space,Str "reference",Str ",",Space,Str "because",Space,Str "it",Space,Str "contains",Space,Str "a",Space,Str "space",Str ".",Str "[",Str "^",Str "my",Space,Str "note",Str "]",Space,Str "Here",Space,Str "is",Space,Str "an",Space,Str "inline",Space,Str "note",Str ".",Note [Para [Str "This",Space,Str "is",Space,Emph [Str "easier"],Space,Str "to",Space,Str "type",Str ".",Space,Str "Inline",Space,Str "notes",Space,Str "may",Space,Str "contain",Space,Link [Str "links"] ("http://google.com",""),Space,Str "and",Space,Code ("",[],[]) "]",Space,Str "verbatim",Space,Str "characters",Str ",",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "[",Str "bracketed",Space,Str "text",Str "]",Str "."]]] +,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.",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",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",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",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",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,",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",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",Space,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",Space,Str "[bracketed",Space,Str "text]."]]] ,BlockQuote - [Para [Str "Notes",Space,Str "can",Space,Str "go",Space,Str "in",Space,Str "quotes",Str ".",Note [Para [Str "In",Space,Str "quote",Str "."]]]] + [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",Str ".",Note [Para [Str "In",Space,Str "list",Str "."]]]]] -,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",Str ",",Space,Str "as",Space,Str "it",Space,Str "is",Space,Str "not",Space,Str "indented",Str "."]] + [[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."]] diff --git a/tests/textile-reader.native b/tests/textile-reader.native index 39359d13a..a97869f06 100644 --- a/tests/textile-reader.native +++ b/tests/textile-reader.native @@ -1,13 +1,13 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) [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",Str ".",Space,Str "Part",Space,Str "of",Space,Str "it",Space,Str "comes",LineBreak,Str "from",Space,Str "John",Space,Str "Gruber",Str "\8217",Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."] ,HorizontalRule -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embeded",Space,Str "link"] ("http://www.example.com","")] -,Header 3 [Str "Level",Space,Str "3",Space,Str "with",Space,Strong [Str "emphasis"]] -,Header 4 [Str "Level",Space,Str "4"] -,Header 5 [Str "Level",Space,Str "5"] -,Header 6 [Str "Level",Space,Str "6"] -,Header 1 [Str "Paragraphs"] +,Header 1 ("",[],[]) [Str "Headers"] +,Header 2 ("",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embeded",Space,Str "link"] ("http://www.example.com","")] +,Header 3 ("",[],[]) [Str "Level",Space,Str "3",Space,Str "with",Space,Strong [Str "emphasis"]] +,Header 4 ("",[],[]) [Str "Level",Space,Str "4"] +,Header 5 ("",[],[]) [Str "Level",Space,Str "5"] +,Header 6 ("",[],[]) [Str "Level",Space,Str "6"] +,Header 1 ("",[],[]) [Str "Paragraphs"] ,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "regular",Space,Str "paragraph",Str "."] ,Para [Str "Line",Space,Str "breaks",Space,Str "are",Space,Str "preserved",Space,Str "in",Space,Str "textile",Str ",",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",Str "."] ,Para [Str "Here",Str "\8217",Str "s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet",Str "."] @@ -16,35 +16,39 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,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",Str "."] ,Para [Str "pandoc",Space,Str "converts",Space,Str "textile",Str "."] -,Header 1 [Str "Block",Space,Str "Quotes"] +,Header 1 ("",[],[]) [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",Str ".",Space,Str "He",Space,Str "had",Space,Str "a",Space,Str "lot",Space,Str "of",Space,Str "things",Space,Str "to",LineBreak,Str "say",Str ",",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",Str "."]] ,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph",Str "."] -,Header 1 [Str "Code",Space,Str "Blocks"] +,Header 1 ("",[],[]) [Str "Code",Space,Str "Blocks"] ,Para [Str "Code",Str ":"] ,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",Str ":"] ,CodeBlock ("",[],[]) " this code block is indented by two tabs\n\n These should not be escaped: \\$ \\\\ \\> \\[ \\{" ,CodeBlock ("",[],[]) "Code block with .bc\n continued\n @</\\\n" ,Para [Str "Inline",Space,Str "code",Str ":",Space,Code ("",[],[]) "<tt>",Str ",",Space,Code ("",[],[]) "@",Str "."] -,Header 1 [Str "Notextile"] +,Header 1 ("",[],[]) [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",Str "."] -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] +,Header 1 ("",[],[]) [Str "Lists"] +,Header 2 ("",[],[]) [Str "Unordered"] ,Para [Str "Asterisks",Space,Str "tight",Str ":"] ,BulletList [[Plain [Str "asterisk",Space,Str "1"]] ,[Plain [Str "asterisk",Space,Str "2"]] ,[Plain [Str "asterisk",Space,Str "3"]]] -,Header 2 [Str "Ordered"] +,Para [Str "With",Space,Str "line",Space,Str "breaks",Str ":"] +,BulletList + [[Plain [Str "asterisk",Space,Str "1",LineBreak,Str "newline"]] + ,[Plain [Str "asterisk",Space,Str "2"]]] +,Header 2 ("",[],[]) [Str "Ordered"] ,Para [Str "Tight",Str ":"] ,OrderedList (1,DefaultStyle,DefaultDelim) [[Plain [Str "First"]] ,[Plain [Str "Second"]] ,[Plain [Str "Third"]]] -,Header 2 [Str "Nested"] +,Header 2 ("",[],[]) [Str "Nested"] ,BulletList [[Plain [Str "ui",Space,Str "1"] ,BulletList @@ -59,7 +63,7 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,BulletList [[Plain [Str "ui",Space,Str "2",Str ".",Str "1",Str ".",Str "1"]] ,[Plain [Str "ui",Space,Str "2",Str ".",Str "1",Str ".",Str "2"]]]]]]] -,Header 2 [Str "Definition",Space,Str "List"] +,Header 2 ("",[],[]) [Str "Definition",Space,Str "List"] ,DefinitionList [([Str "coffee"], [[Plain [Str "Hot",Space,Str "and",Space,Str "black"]]]) @@ -70,22 +74,23 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,Para [Str "Cold",Space,Str "drink",Space,Str "that",Space,Str "goes",Space,Str "great",Space,Str "with",Space,Str "cookies",Str "."]]]) ,([Str "beer"], [[Plain [Str "fresh",Space,Str "and",Space,Str "bitter"]]])] -,Header 1 [Str "Inline",Space,Str "Markup"] +,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 ".",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 "Hyphenated-words-are-ok",Str ",",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "strange_underscore_notation",Str ".",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",Str "."]],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",Str ":",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Superscript [Strong [Str "hello"]],Space,Str "a",Superscript [Str "hello",Space,Str "there"],Str ".",LineBreak,Str "Subscripts",Str ":",Space,Subscript [Str "here"],Space,Str "H",Subscript [Str "2"],Str "O",Str ",",Space,Str "H",Subscript [Str "23"],Str "O",Str ",",Space,Str "H",Subscript [Str "many",Space,Str "of",Space,Str "them"],Str "O",Str "."] ,Para [Str "Dashes",Space,Str ":",Space,Str "How",Space,Str "cool",Space,Str "\8212",Space,Str "automatic",Space,Str "dashes",Str "."] ,Para [Str "Elipses",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",Str "."] ,Para [Str "Quotes",Space,Str "and",Space,Str "apostrophes",Space,Str ":",Space,Quoted DoubleQuote [Str "I",Str "\8217",Str "d",Space,Str "like",Space,Str "to",Space,Str "thank",Space,Str "you"],Space,Str "for",Space,Str "example",Str "."] -,Header 1 [Str "Links"] -,Header 2 [Str "Explicit"] +,Header 1 ("",[],[]) [Str "Links"] +,Header 2 ("",[],[]) [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 [Str "Automatic",Space,Str "linking",Space,Str "to",Space,Link [Str "http://www.example.com"] ("http://www.example.com",""),Space,Str "and",Space,Link [Str "foobar@example.com"] ("mailto:foobar@example.com",""),Str "."] +,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",Str "."] -,Header 1 [Str "Tables"] +,Para [Str "A",Space,Str "link",Link [Str "with",Space,Str "brackets"] ("http://www.example.com",""),Str "and",Space,Str "no",Space,Str "spaces",Str "."] +,Header 1 ("",[],[]) [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 [Str "Without",Space,Str "headers"] +,Header 2 ("",[],[]) [Str "Without",Space,Str "headers"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] [] [[[Plain [Str "name"]] @@ -101,7 +106,7 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,[Plain [Str "45"]] ,[Plain [Str "f"]]]] ,Para [Str "and",Space,Str "some",Space,Str "text",Space,Str "following",Space,Str "\8230"] -,Header 2 [Str "With",Space,Str "headers"] +,Header 2 ("",[],[]) [Str "With",Space,Str "headers"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] [[Plain [Str "name"]] ,[Plain [Str "age"]] @@ -115,10 +120,10 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,[[Plain [Str "bella"]] ,[Plain [Str "45"]] ,[Plain [Str "f"]]]] -,Header 1 [Str "Images"] +,Header 1 ("",[],[]) [Str "Images"] ,Para [Str "Textile",Space,Str "inline",Space,Str "image",Space,Str "syntax",Str ",",Space,Str "like",Space,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 [Str "Attributes"] -,Header 2 [Str "HTML",Space,Str "and",Space,Str "CSS",Space,Str "attributes",Space,Str "are",Space,Str "ignored"] +,Header 1 ("",[],[]) [Str "Attributes"] +,Header 2 ("",[],[]) [Str "HTML",Space,Str "and",Space,Str "CSS",Space,Str "attributes",Space,Str "are",Space,Str "ignored"] ,Para [Str "as",Space,Str "well",Space,Str "as",Space,Strong [Str "inline",Space,Str "attributes"],Space,Str "of",Space,Str " all kind"] ,Para [Str "and",Space,Str "paragraph",Space,Str "attributes",Str ",",Space,Str "and",Space,Str "table",Space,Str "attributes",Str "."] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] @@ -129,7 +134,7 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) ,[[Plain [Str "joan"]] ,[Plain [Str "24"]] ,[Plain [Str "f"]]]] -,Header 1 [Str "Raw",Space,Str "HTML"] +,Header 1 ("",[],[]) [Str "Raw",Space,Str "HTML"] ,Para [Str "However",Str ",",Space,RawInline "html" "<strong>",Space,Str "raw",Space,Str "HTML",Space,Str "inlines",Space,RawInline "html" "</strong>",Space,Str "are",Space,Str "accepted",Str ",",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str ":"] ,RawBlock "html" "<div class=\"foobar\">" ,Para [Str "any",Space,Strong [Str "Raw",Space,Str "HTML",Space,Str "Block"],Space,Str "with",Space,Str "bold",LineBreak] @@ -143,15 +148,18 @@ Pandoc (Meta {docTitle = [], docAuthors = [], docDate = []}) [[Plain [Str "this",Space,Str "<",Str "div",Str ">",Space,Str "won",Str "\8217",Str "t",Space,Str "produce",Space,Str "raw",Space,Str "html",Space,Str "blocks",Space,Str "<",Str "/div",Str ">"]] ,[Plain [Str "but",Space,Str "this",Space,RawInline "html" "<strong>",Space,Str "will",Space,Str "produce",Space,Str "inline",Space,Str "html",Space,RawInline "html" "</strong>"]]] ,Para [Str "Can",Space,Str "you",Space,Str "prove",Space,Str "that",Space,Str "2",Space,Str "<",Space,Str "3",Space,Str "?"] -,Header 1 [Str "Raw",Space,Str "LaTeX"] +,Header 1 ("",[],[]) [Str "Raw",Space,Str "LaTeX"] ,Para [Str "This",Space,Str "Textile",Space,Str "reader",Space,Str "also",Space,Str "accepts",Space,Str "raw",Space,Str "LaTeX",Space,Str "for",Space,Str "blocks",Space,Str ":"] ,RawBlock "latex" "\\begin{itemize}\n \\item one\n \\item two\n\\end{itemize}" ,Para [Str "and",Space,Str "for",Space,RawInline "latex" "\\emph{inlines}",Str "."] -,Header 1 [Str "Acronyms",Space,Str "and",Space,Str "marks"] +,Header 1 ("",[],[]) [Str "Acronyms",Space,Str "and",Space,Str "marks"] ,Para [Str "PBS (Public Broadcasting System)"] ,Para [Str "Hi",Str "\8482"] ,Para [Str "Hi",Space,Str "\8482"] ,Para [Str "\174",Space,Str "Hi",Str "\174"] ,Para [Str "Hi",Str "\169",Str "2008",Space,Str "\169",Space,Str "2008"] -,Header 1 [Str "Footnotes"] -,Para [Str "A",Space,Str "note",Str ".",Note [Para [Str "The",Space,Str "note",LineBreak,Str "is",Space,Str "here",Str "!"]]]] +,Header 1 ("",[],[]) [Str "Footnotes"] +,Para [Str "A",Space,Str "note",Str ".",Note [Para [Str "The",Space,Str "note",LineBreak,Str "is",Space,Str "here",Str "!"]],Space,Str "Another",Space,Str "note",Note [Para [Str "Other",Space,Str "note",Str "."]],Str "."] +,Header 1 ("",[],[]) [Str "Comment",Space,Str "blocks"] +,Null +,Para [Str "not",Space,Str "a",Space,Str "comment",Str "."]] diff --git a/tests/textile-reader.textile b/tests/textile-reader.textile index adfec90d3..067cf690a 100644 --- a/tests/textile-reader.textile +++ b/tests/textile-reader.textile @@ -91,6 +91,12 @@ Asterisks tight: * asterisk 2 * asterisk 3 +With line breaks: + +* asterisk 1 +newline +* asterisk 2 + h2. Ordered Tight: @@ -151,10 +157,12 @@ Just a "url":http://www.url.com "Email link":mailto:nobody@nowhere.net -Automatic linking to http://www.example.com and foobar@example.com. +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. + h1. Tables Textile allows tables with and without headers : @@ -235,7 +243,16 @@ Hi(c)2008 (C) 2008 h1. Footnotes -A note.[1] +A note.[1] Another note[2]. fn1. The note is here! + +fn2. Other note. + +h1. Comment blocks + +###. my comment +is here. + +not a comment. diff --git a/tests/writer.asciidoc b/tests/writer.asciidoc index 440127379..fbe0036d8 100644 --- a/tests/writer.asciidoc +++ b/tests/writer.asciidoc @@ -9,31 +9,40 @@ markdown test suite. ''''' +[[headers]] Headers ------- +[[level-2-with-an-embedded-link]] Level 2 with an link:/url[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 ~~~~~~~ @@ -41,6 +50,7 @@ with no blank line ''''' +[[paragraphs]] Paragraphs ---------- @@ -57,6 +67,7 @@ here. ''''' +[[block-quotes]] Block Quotes ------------ @@ -100,6 +111,7 @@ And a following paragraph. ''''' +[[code-blocks]] Code Blocks ----------- @@ -125,9 +137,11 @@ These should not be escaped: \$ \\ \> \[ \{ ''''' +[[lists]] Lists ----- +[[unordered]] Unordered ~~~~~~~~~ @@ -167,6 +181,7 @@ Minuses loose: * Minus 2 * Minus 3 +[[ordered]] Ordered ~~~~~~~ @@ -202,6 +217,7 @@ Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. 2. Item 2. 3. Item 3. +[[nested]] Nested ~~~~~~ @@ -227,6 +243,7 @@ Same thing but with paragraphs: * Foe 3. Third +[[tabs-and-spaces]] Tabs and spaces ~~~~~~~~~~~~~~~ @@ -235,6 +252,7 @@ Tabs and 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 ~~~~~~~~~~~~~~~~~~ @@ -268,6 +286,7 @@ B. Williams ''''' +[[definition-lists]] Definition Lists ---------------- @@ -349,6 +368,7 @@ orange:: 1. sublist 2. sublist +[[html-blocks]] HTML Blocks ----------- @@ -405,6 +425,7 @@ Hr’s: ''''' +[[inline-markup]] Inline Markup ------------- @@ -435,6 +456,7 @@ spaces: a^b c^d, a~b c~d. ''''' +[[smart-quotes-ellipses-dashes]] Smart quotes, ellipses, dashes ------------------------------ @@ -457,6 +479,7 @@ Ellipses…and…and…. ''''' +[[latex]] LaTeX ----- @@ -483,6 +506,7 @@ Here’s a LaTeX table: ''''' +[[special-characters]] Special Characters ------------------ @@ -538,9 +562,11 @@ Minus: - ''''' +[[links]] Links ----- +[[explicit]] Explicit ~~~~~~~~ @@ -562,6 +588,7 @@ mailto:nobody@nowhere.net[Email link] link:[Empty]. +[[reference]] Reference ~~~~~~~~~ @@ -591,6 +618,7 @@ Foo link:/url/[bar]. Foo link:/url/[biz]. +[[with-ampersands]] With ampersands ~~~~~~~~~~~~~~~ @@ -602,6 +630,7 @@ 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]] Autolinks ~~~~~~~~~ @@ -625,6 +654,7 @@ or here: <http://example.com/> ''''' +[[images]] Images ------ @@ -636,6 +666,7 @@ Here is a movie image:movie.jpg[movie] icon. ''''' +[[footnotes]] Footnotes --------- diff --git a/tests/writer.context b/tests/writer.context index bb070ce67..114d00b3c 100644 --- a/tests/writer.context +++ b/tests/writer.context @@ -6,7 +6,7 @@ % Enable hyperlinks \setupinteraction[state=start, color=middleblue] -\setuppapersize [letter][letter] +\setuppapersize [letter][letter] \setuplayout [width=middle, backspace=1.5in, cutspace=1.5in, height=middle, topspace=0.75in, bottomspace=0.75in] @@ -115,7 +115,7 @@ sub status { A list: -\startitemize[n][stopper=.] +\startitemize[n,packed][stopper=.] \item item one \item @@ -169,7 +169,7 @@ These should not be escaped: \$ \\ \> \[ \{ Asterisks tight: -\startitemize +\startitemize[packed] \item asterisk 1 \item @@ -191,7 +191,7 @@ Asterisks loose: Pluses tight: -\startitemize +\startitemize[packed] \item Plus 1 \item @@ -213,7 +213,7 @@ Pluses loose: Minuses tight: -\startitemize +\startitemize[packed] \item Minus 1 \item @@ -237,7 +237,7 @@ Minuses loose: Tight: -\startitemize[n][stopper=.] +\startitemize[n,packed][stopper=.] \item First \item @@ -248,7 +248,7 @@ Tight: and: -\startitemize[n][stopper=.] +\startitemize[n,packed][stopper=.] \item One \item @@ -294,13 +294,13 @@ Multiple paragraphs: \subsection[nested]{Nested} -\startitemize +\startitemize[packed] \item Tab - \startitemize + \startitemize[packed] \item Tab - \startitemize + \startitemize[packed] \item Tab \stopitemize @@ -309,12 +309,12 @@ Multiple paragraphs: Here's another: -\startitemize[n][stopper=.] +\startitemize[n,packed][stopper=.] \item First \item Second: - \startitemize + \startitemize[packed] \item Fee \item @@ -334,7 +334,7 @@ Same thing but with paragraphs: \item Second: - \startitemize + \startitemize[packed] \item Fee \item @@ -372,12 +372,12 @@ Same thing but with paragraphs: with a continuation - \startitemize[r][start=4,stopper=.,width=2.0em] + \startitemize[r,packed][start=4,stopper=.,width=2.0em] \item sublist with roman numerals, starting with 4 \item more items - \startitemize[A][left=(,stopper=),width=2.0em] + \startitemize[A,packed][left=(,stopper=),width=2.0em] \item a subsublist \item @@ -388,16 +388,16 @@ Same thing but with paragraphs: Nesting: -\startitemize[A][stopper=.] +\startitemize[A,packed][stopper=.] \item Upper Alpha - \startitemize[R][stopper=.] + \startitemize[R,packed][stopper=.] \item Upper Roman. - \startitemize[n][start=6,left=(,stopper=),width=2.0em] + \startitemize[n,packed][start=6,left=(,stopper=),width=2.0em] \item Decimal start with 6 - \startitemize[a][start=3,stopper=)] + \startitemize[a,packed][start=3,stopper=)] \item Lower alpha with paren \stopitemize @@ -407,12 +407,12 @@ Nesting: Autonumbering: -\startitemize[n] +\startitemize[n,packed] \item Autonumber. \item More. - \startitemize[a] + \startitemize[a,packed] \item Nested. \stopitemize @@ -529,7 +529,7 @@ Blank line after term, indented marker, alternate markers: \startdescription{orange} orange fruit - \startitemize[n][stopper=.] + \startitemize[n,packed][stopper=.] \item sublist \item @@ -646,7 +646,7 @@ Ellipses\ldots{}and\ldots{}and\ldots{}. \section[latex]{LaTeX} -\startitemize +\startitemize[packed] \item \cite[22-23]{smith.1899} \item @@ -668,7 +668,7 @@ Ellipses\ldots{}and\ldots{}and\ldots{}. These shouldn't be math: -\startitemize +\startitemize[packed] \item To get the famous equation, write \type{$e = mc^2$}. \item @@ -688,7 +688,7 @@ Here's a LaTeX table: Here is some unicode: -\startitemize +\startitemize[packed] \item I hat: Î \item @@ -813,7 +813,7 @@ braces]\from[url26]. With an ampersand: \useURL[url27][http://example.com/?foo=1&bar=2][][\hyphenatedurl{http://example.com/?foo=1&bar=2}]\from[url27] -\startitemize +\startitemize[packed] \item In a list? \item @@ -873,7 +873,7 @@ note{]} Here is an inline note.\footnote{This is {\em easier} to type. Inline Notes can go in quotes.\footnote{In quote.} \stopblockquote -\startitemize[n][stopper=.] +\startitemize[n,packed][stopper=.] \item And in list items.\footnote{In list.} \stopitemize diff --git a/tests/writer.docbook b/tests/writer.docbook index 0eeaebbfd..54d27f789 100644 --- a/tests/writer.docbook +++ b/tests/writer.docbook @@ -1323,7 +1323,7 @@ These should not be escaped: \$ \\ \> \[ \{ <title>Autolinks</title> <para> With an ampersand: - <ulink url="http://example.com/?foo=1&bar=2"><literal>http://example.com/?foo=1&bar=2</literal></ulink> + <ulink url="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</ulink> </para> <itemizedlist> <listitem> @@ -1333,7 +1333,7 @@ These should not be escaped: \$ \\ \> \[ \{ </listitem> <listitem> <para> - <ulink url="http://example.com/"><literal>http://example.com/</literal></ulink> + <ulink url="http://example.com/">http://example.com/</ulink> </para> </listitem> <listitem> @@ -1348,7 +1348,7 @@ These should not be escaped: \$ \\ \> \[ \{ <blockquote> <para> Blockquoted: - <ulink url="http://example.com/"><literal>http://example.com/</literal></ulink> + <ulink url="http://example.com/">http://example.com/</ulink> </para> </blockquote> <para> diff --git a/tests/writer.fb2 b/tests/writer.fb2 new file mode 100644 index 000000000..0bcbf1c2a --- /dev/null +++ b/tests/writer.fb2 @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info><book-title>Pandoc Test Suite</book-title><author><first-name>John</first-name><last-name>MacFarlane</last-name></author><author><nickname>Anonymous</nickname></author><date>July 17, 2006</date></title-info><document-info><program-used>pandoc</program-used></document-info></description><body><title><p>Pandoc Test Suite</p></title><annotation><p>John MacFarlane</p><p>Anonymous</p><p>July 17, 2006</p></annotation><section><p>This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Headers</p></title><section><title><p>Level 2 with an embedded link </url></p></title><section><title><p>Level 3 with emphasis</p></title><section><title><p>Level 4</p></title><section><title><p>Level 5</p></title></section></section></section></section></section><section><title><p>Level 1</p></title><section><title><p>Level 2 with emphasis</p></title><section><title><p>Level 3</p></title><p>with no blank line</p></section></section><section><title><p>Level 2</p></title><p>with no blank line</p><empty-line /><p>——————————</p><empty-line /></section></section><section><title><p>Paragraphs</p></title><p>Here’s a regular paragraph.</p><p>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.</p><p>Here’s one with a bullet. * criminey.</p><p>There should be a hard line break<empty-line />here.</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Block Quotes</p></title><p>E-mail style:</p><cite><p>This is a block quote. It is pretty short.</p></cite><cite><p>Code in a block quote:</p><empty-line /><p><code>sub status {</code></p><p><code> print "working";</code></p><p><code>}</code></p><empty-line /><p>A list:</p><p> 1. item one</p><p> 2. item two</p><p>Nested block quotes:</p><cite><p>nested</p></cite><cite><p>nested</p></cite></cite><p>This should not be a block quote: 2 > 1.</p><p>And a following paragraph.</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Code Blocks</p></title><p>Code:</p><empty-line /><p><code>---- (should be four hyphens)</code></p><p><code></code></p><p><code>sub status {</code></p><p><code> print "working";</code></p><p><code>}</code></p><p><code></code></p><p><code>this code block is indented by one tab</code></p><empty-line /><p>And:</p><empty-line /><p><code> this code block is indented by two tabs</code></p><p><code></code></p><p><code>These should not be escaped: \$ \\ \> \[ \{</code></p><empty-line /><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Lists</p></title><section><title><p>Unordered</p></title><p>Asterisks tight:</p><p>• asterisk 1</p><p>• asterisk 2</p><p>• asterisk 3</p><p>Asterisks loose:</p><p>• asterisk 1<empty-line /></p><p>• asterisk 2<empty-line /></p><p>• asterisk 3<empty-line /></p><p>Pluses tight:</p><p>• Plus 1</p><p>• Plus 2</p><p>• Plus 3</p><p>Pluses loose:</p><p>• Plus 1<empty-line /></p><p>• Plus 2<empty-line /></p><p>• Plus 3<empty-line /></p><p>Minuses tight:</p><p>• Minus 1</p><p>• Minus 2</p><p>• Minus 3</p><p>Minuses loose:</p><p>• Minus 1<empty-line /></p><p>• Minus 2<empty-line /></p><p>• Minus 3<empty-line /></p></section><section><title><p>Ordered</p></title><p>Tight:</p><p> 1. First</p><p> 2. Second</p><p> 3. Third</p><p>and:</p><p> 1. One</p><p> 2. Two</p><p> 3. Three</p><p>Loose using tabs:</p><p> 1. First<empty-line /></p><p> 2. Second<empty-line /></p><p> 3. Third<empty-line /></p><p>and using spaces:</p><p> 1. One<empty-line /></p><p> 2. Two<empty-line /></p><p> 3. Three<empty-line /></p><p>Multiple paragraphs:</p><p> 1. Item 1, graf one.<empty-line />Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.<empty-line /></p><p> 2. Item 2.<empty-line /></p><p> 3. Item 3.<empty-line /></p></section><section><title><p>Nested</p></title><p>• Tab<p>◦ Tab<p>* Tab</p></p></p><p>Here’s another:</p><p> 1. First</p><p> 2. Second:<p> • Fee</p><p> • Fie</p><p> • Foe</p></p><p> 3. Third</p><p>Same thing but with paragraphs:</p><p> 1. First<empty-line /></p><p> 2. Second:<empty-line /><p> • Fee</p><p> • Fie</p><p> • Foe</p></p><p> 3. Third<empty-line /></p></section><section><title><p>Tabs and spaces</p></title><p>• this is a list item indented with tabs<empty-line /></p><p>• this is a list item indented with spaces<empty-line /><p>◦ this is an example list item indented with tabs<empty-line /></p><p>◦ this is an example list item indented with spaces<empty-line /></p></p></section><section><title><p>Fancy list markers</p></title><p> (2) begins with 2</p><p> (3) and now 3<empty-line />with a continuation<empty-line /><p> (3) iv. sublist with roman numerals, starting with 4</p><p> (3) v. more items<p> (3) v. (A) a subsublist</p><p> (3) v. (B) a subsublist</p></p></p><p>Nesting:</p><p> A. Upper Alpha<p> A. I. Upper Roman.<p> A. I. (6) Decimal start with 6<p> A. I. (6) c) Lower alpha with paren</p></p></p></p><p>Autonumbering:</p><p> 1. Autonumber.</p><p> 2. More.<p> 2. 1. Nested.</p></p><p>Should not be a list item:</p><p>M.A. 2007</p><p>B. Williams</p><empty-line /><p>——————————</p><empty-line /></section></section><section><title><p>Definition Lists</p></title><p>Tight using spaces:</p><p><strong>apple</strong></p><p> red fruit<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /></p><p><strong>banana</strong></p><p> yellow fruit<empty-line /></p><p>Tight using tabs:</p><p><strong>apple</strong></p><p> red fruit<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /></p><p><strong>banana</strong></p><p> yellow fruit<empty-line /></p><p>Loose:</p><p><strong>apple</strong></p><p> red fruit<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /></p><p><strong>banana</strong></p><p> yellow fruit<empty-line /></p><p>Multiple blocks with italics:</p><p><strong><emphasis>apple</emphasis></strong></p><p> red fruit<empty-line /> contains seeds, crisp, pleasant to taste<empty-line /></p><p><strong><emphasis>orange</emphasis></strong></p><p> orange fruit<empty-line /><empty-line /><p><code> { orange code block }</code></p><empty-line /><cite><p> orange block quote</p></cite></p><p>Multiple definitions, tight:</p><p><strong>apple</strong></p><p> red fruit<empty-line /> computer<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /> bank<empty-line /></p><p>Multiple definitions, loose:</p><p><strong>apple</strong></p><p> red fruit<empty-line /> computer<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /> bank<empty-line /></p><p>Blank line after term, indented marker, alternate markers:</p><p><strong>apple</strong></p><p> red fruit<empty-line /> computer<empty-line /></p><p><strong>orange</strong></p><p> orange fruit<empty-line /><p> 1. sublist</p><p> 2. sublist</p></p></section><section><title><p>HTML Blocks</p></title><p>Simple block on one line:</p><empty-line /><p><code><div></code></p><empty-line />foo<empty-line /><p><code></div></code></p><empty-line /><p>And nested without indentation:</p><empty-line /><p><code><div></code></p><p><code><div></code></p><p><code><div></code></p><empty-line />foo<empty-line /><p><code></div></code></p><p><code></div></code></p><p><code><div></code></p><empty-line />bar<empty-line /><p><code></div></code></p><p><code></div></code></p><empty-line /><p>Interpreted markdown in a table:</p><empty-line /><p><code><table></code></p><p><code><tr></code></p><p><code><td></code></p><empty-line />This is <emphasis>emphasized</emphasis><empty-line /><p><code></td></code></p><p><code><td></code></p><empty-line />And this is <strong>strong</strong><empty-line /><p><code></td></code></p><p><code></tr></code></p><p><code></table></code></p><p><code></code></p><p><code><script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script></code></p><empty-line /><p>Here’s a simple block:</p><empty-line /><p><code><div></code></p><p><code> </code></p><empty-line />foo<empty-line /><p><code></div></code></p><empty-line /><p>This should be a code block, though:</p><empty-line /><p><code><div></code></p><p><code> foo</code></p><p><code></div></code></p><empty-line /><p>As should this:</p><empty-line /><p><code><div>foo</div></code></p><empty-line /><p>Now, nested:</p><empty-line /><p><code><div></code></p><p><code> <div></code></p><p><code> <div></code></p><p><code> </code></p><empty-line />foo<empty-line /><p><code></div></code></p><p><code> </div></code></p><p><code></div></code></p><empty-line /><p>This should just be an HTML comment:</p><empty-line /><p><code><!-- Comment --></code></p><empty-line /><p>Multiline:</p><empty-line /><p><code><!--</code></p><p><code>Blah</code></p><p><code>Blah</code></p><p><code>--></code></p><p><code></code></p><p><code><!--</code></p><p><code> This is another comment.</code></p><p><code>--></code></p><empty-line /><p>Code block:</p><empty-line /><p><code><!-- Comment --></code></p><empty-line /><p>Just plain comment, with trailing spaces on the line:</p><empty-line /><p><code><!-- foo --> </code></p><empty-line /><p>Code:</p><empty-line /><p><code><hr /></code></p><empty-line /><p>Hr’s:</p><empty-line /><p><code><hr></code></p><p><code></code></p><p><code><hr /></code></p><p><code></code></p><p><code><hr /></code></p><p><code></code></p><p><code><hr> </code></p><p><code></code></p><p><code><hr /> </code></p><p><code></code></p><p><code><hr /> </code></p><p><code></code></p><p><code><hr class="foo" id="bar" /></code></p><p><code></code></p><p><code><hr class="foo" id="bar" /></code></p><p><code></code></p><p><code><hr class="foo" id="bar"></code></p><empty-line /><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Inline Markup</p></title><p>This is <emphasis>emphasized</emphasis>, and so <emphasis>is this</emphasis>.</p><p>This is <strong>strong</strong>, and so <strong>is this</strong>.</p><p>An <emphasis>emphasized link<a l:href="#l1" type="note"><sup>[1]</sup></a></emphasis>.</p><p><strong><emphasis>This is strong and em.</emphasis></strong></p><p>So is <strong><emphasis>this</emphasis></strong> word.</p><p><strong><emphasis>This is strong and em.</emphasis></strong></p><p>So is <strong><emphasis>this</emphasis></strong> word.</p><p>This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code><html></code>.</p><p><strikethrough>This is <emphasis>strikeout</emphasis>.</strikethrough></p><p>Superscripts: a<sup>bc</sup>d a<sup><emphasis>hello</emphasis></sup> a<sup>hello there</sup>.</p><p>Subscripts: H<sub>2</sub>O, H<sub>23</sub>O, H<sub>many of them</sub>O.</p><p>These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Smart quotes, ellipses, dashes</p></title><p>“Hello,” said the spider. “‘Shelob’ is my name.”</p><p>‘A’, ‘B’, and ‘C’ are letters.</p><p>‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’</p><p>‘He said, “I want to go.”’ Were you alive in the 70’s?</p><p>Here is some quoted ‘<code>code</code>’ and a “quoted link<a l:href="#l2" type="note"><sup>[2]</sup></a>”.</p><p>Some dashes: one—two — three—four — five.</p><p>Dashes between numbers: 5–7, 255–66, 1987–1999.</p><p>Ellipses…and…and….</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>LaTeX</p></title><p>• </p><p>• <code>2+2=4</code></p><p>• <code>x \in y</code></p><p>• <code>\alpha \wedge \omega</code></p><p>• <code>223</code></p><p>• <code>p</code>-Tree</p><p>• Here’s some display math: <code>\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}</code></p><p>• Here’s one that has a line break in it: <code>\alpha + \omega \times x^2</code>.</p><p>These shouldn’t be math:</p><p>• To get the famous equation, write <code>$e = mc^2$</code>.</p><p>• $22,000 is a <emphasis>lot</emphasis> of money. So is $34,000. (It worked if “lot” is emphasized.)</p><p>• Shoes ($20) and socks ($5).</p><p>• Escaped <code>$</code>: $73 <emphasis>this should be emphasized</emphasis> 23$.</p><p>Here’s a LaTeX table:</p><empty-line /><p><code>\begin{tabular}{|l|l|}\hline</code></p><p><code>Animal & Number \\ \hline</code></p><p><code>Dog & 2 \\</code></p><p><code>Cat & 1 \\ \hline</code></p><p><code>\end{tabular}</code></p><empty-line /><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Special Characters</p></title><p>Here is some unicode:</p><p>• I hat: Î</p><p>• o umlaut: ö</p><p>• section: §</p><p>• set membership: ∈</p><p>• copyright: ©</p><p>AT&T has an ampersand in their name.</p><p>AT&T is another way to write it.</p><p>This & that.</p><p>4 < 5.</p><p>6 > 5.</p><p>Backslash: \</p><p>Backtick: `</p><p>Asterisk: *</p><p>Underscore: _</p><p>Left brace: {</p><p>Right brace: }</p><p>Left bracket: [</p><p>Right bracket: ]</p><p>Left paren: (</p><p>Right paren: )</p><p>Greater-than: ></p><p>Hash: #</p><p>Period: .</p><p>Bang: !</p><p>Plus: +</p><p>Minus: -</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Links</p></title><section><title><p>Explicit</p></title><p>Just a URL<a l:href="#l3" type="note"><sup>[3]</sup></a>.</p><p>URL and title<a l:href="#l4" type="note"><sup>[4]</sup></a>.</p><p>URL and title<a l:href="#l5" type="note"><sup>[5]</sup></a>.</p><p>URL and title<a l:href="#l6" type="note"><sup>[6]</sup></a>.</p><p>URL and title<a l:href="#l7" type="note"><sup>[7]</sup></a></p><p>URL and title<a l:href="#l8" type="note"><sup>[8]</sup></a></p><p>with_underscore<a l:href="#l9" type="note"><sup>[9]</sup></a></p><p>Email link<a l:href="#l10" type="note"><sup>[10]</sup></a></p><p>Empty<a l:href="#l11" type="note"><sup>[11]</sup></a>.</p></section><section><title><p>Reference</p></title><p>Foo bar<a l:href="#l12" type="note"><sup>[12]</sup></a>.</p><p>Foo bar<a l:href="#l13" type="note"><sup>[13]</sup></a>.</p><p>Foo bar<a l:href="#l14" type="note"><sup>[14]</sup></a>.</p><p>With embedded [brackets]<a l:href="#l15" type="note"><sup>[15]</sup></a>.</p><p>b<a l:href="#l16" type="note"><sup>[16]</sup></a> by itself should be a link.</p><p>Indented once<a l:href="#l17" type="note"><sup>[17]</sup></a>.</p><p>Indented twice<a l:href="#l18" type="note"><sup>[18]</sup></a>.</p><p>Indented thrice<a l:href="#l19" type="note"><sup>[19]</sup></a>.</p><p>This should [not][] be a link.</p><empty-line /><p><code>[not]: /url</code></p><empty-line /><p>Foo bar<a l:href="#l20" type="note"><sup>[20]</sup></a>.</p><p>Foo biz<a l:href="#l21" type="note"><sup>[21]</sup></a>.</p></section><section><title><p>With ampersands</p></title><p>Here’s a link with an ampersand in the URL<a l:href="#l22" type="note"><sup>[22]</sup></a>.</p><p>Here’s a link with an amersand in the link text: AT&T<a l:href="#l23" type="note"><sup>[23]</sup></a>.</p><p>Here’s an inline link<a l:href="#l24" type="note"><sup>[24]</sup></a>.</p><p>Here’s an inline link in pointy braces<a l:href="#l25" type="note"><sup>[25]</sup></a>.</p></section><section><title><p>Autolinks</p></title><p>With an ampersand: http://example.com/?foo=1&bar=2<a l:href="#l26" type="note"><sup>[26]</sup></a></p><p>• In a list?</p><p>• http://example.com/<a l:href="#l27" type="note"><sup>[27]</sup></a></p><p>• It should.</p><p>An e-mail address: nobody@nowhere.net<a l:href="#l28" type="note"><sup>[28]</sup></a></p><cite><p>Blockquoted: http://example.com/<a l:href="#l29" type="note"><sup>[29]</sup></a></p></cite><p>Auto-links should not occur here: <code><http://example.com/></code></p><empty-line /><p><code>or here: <http://example.com/></code></p><empty-line /><empty-line /><p>——————————</p><empty-line /></section></section><section><title><p>Images</p></title><p>From “Voyage dans la Lune” by Georges Melies (1902):</p><image l:href="#image1" l:type="imageType" alt="lalune" title="Voyage dans la Lune" /><p>Here is a movie <image l:href="#image2" l:type="inlineImageType" alt="movie" /> icon.</p><empty-line /><p>——————————</p><empty-line /></section><section><title><p>Footnotes</p></title><p>Here is a footnote reference,<a l:href="#n30" type="note"><sup>[30]</sup></a> and another.<a l:href="#n31" type="note"><sup>[31]</sup></a> This should <emphasis>not</emphasis> be a footnote reference, because it contains a space.[^my note] Here is an inline note.<a l:href="#n32" type="note"><sup>[32]</sup></a></p><cite><p>Notes can go in quotes.<a l:href="#n33" type="note"><sup>[33]</sup></a></p></cite><p> 1. And in list items.<a l:href="#n34" type="note"><sup>[34]</sup></a></p><p>This paragraph should not be part of the note, as it is not indented.</p></section></body><body name="notes"><section id="l1"><title><p>1</p></title><p><code>/url</code></p></section><section id="l2"><title><p>2</p></title><p><code>http://example.com/?foo=1&bar=2</code></p></section><section id="l3"><title><p>3</p></title><p><code>/url/</code></p></section><section id="l4"><title><p>4</p></title><p>title: <code>/url/</code></p></section><section id="l5"><title><p>5</p></title><p>title preceded by two spaces: <code>/url/</code></p></section><section id="l6"><title><p>6</p></title><p>title preceded by a tab: <code>/url/</code></p></section><section id="l7"><title><p>7</p></title><p>title with "quotes" in it: <code>/url/</code></p></section><section id="l8"><title><p>8</p></title><p>title with single quotes: <code>/url/</code></p></section><section id="l9"><title><p>9</p></title><p><code>/url/with_underscore</code></p></section><section id="l10"><title><p>10</p></title><p><code>mailto:nobody@nowhere.net</code></p></section><section id="l11"><title><p>11</p></title><p><code></code></p></section><section id="l12"><title><p>12</p></title><p><code>/url/</code></p></section><section id="l13"><title><p>13</p></title><p><code>/url/</code></p></section><section id="l14"><title><p>14</p></title><p><code>/url/</code></p></section><section id="l15"><title><p>15</p></title><p><code>/url/</code></p></section><section id="l16"><title><p>16</p></title><p><code>/url/</code></p></section><section id="l17"><title><p>17</p></title><p><code>/url</code></p></section><section id="l18"><title><p>18</p></title><p><code>/url</code></p></section><section id="l19"><title><p>19</p></title><p><code>/url</code></p></section><section id="l20"><title><p>20</p></title><p>Title with "quotes" inside: <code>/url/</code></p></section><section id="l21"><title><p>21</p></title><p>Title with "quote" inside: <code>/url/</code></p></section><section id="l22"><title><p>22</p></title><p><code>http://example.com/?foo=1&bar=2</code></p></section><section id="l23"><title><p>23</p></title><p>AT&T: <code>http://att.com/</code></p></section><section id="l24"><title><p>24</p></title><p><code>/script?foo=1&bar=2</code></p></section><section id="l25"><title><p>25</p></title><p><code>/script?foo=1&bar=2</code></p></section><section id="l26"><title><p>26</p></title><p><code>http://example.com/?foo=1&bar=2</code></p></section><section id="l27"><title><p>27</p></title><p><code>http://example.com/</code></p></section><section id="l28"><title><p>28</p></title><p><code>mailto:nobody@nowhere.net</code></p></section><section id="l29"><title><p>29</p></title><p><code>http://example.com/</code></p></section><section id="n30"><title><p>30</p></title><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></section><section id="n31"><title><p>31</p></title><p>Here’s the long note. This one contains multiple blocks.</p><p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p><empty-line /><p><code> { <code> }</code></p><empty-line /><p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</p></section><section id="n32"><title><p>32</p></title><p>This is <emphasis>easier</emphasis> to type. Inline notes may contain links<a l:href="#l32" type="note"><sup>[32]</sup></a> and <code>]</code> verbatim characters, as well as [bracketed text].</p></section><section id="n33"><title><p>33</p></title><p>In quote.</p></section><section id="n34"><title><p>34</p></title><p>In list.</p></section></body><binary id="image2" content-type="image/jpeg">/9j/4AAQSkZJRgABAQEASABIAAD//gBQVGhpcyBhcnQgaXMgaW4gdGhlIHB1YmxpYyBkb21haW4uIEtldmluIEh1Z2hlcywga2V2aW5oQGVpdC5jb20sIFNlcHRlbWJlciAxOTk1/9sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/8AAEQgAFgAUAwEiAAIRAQMRAf/EABoAAQACAwEAAAAAAAAAAAAAAAAICQUGCgf/xAAjEAABBQEAAwABBQAAAAAAAAAGAwQFBwgCAAEJChEVOXa3/8QAFgEBAQEAAAAAAAAAAAAAAAAABggA/8QAJhEBAAECBQEJAAAAAAAAAAAAAQIAAwQFBhEhszE0NlFUcXR1tP/aAAwDAQACEQMRAD8AqQzziPNmpiqnIO1q4H+WkB84MdlzRSuM82/jVw/JCORtRmQz5d2VTy6WmS2eSYx3U/qkSRbgFsqRzH2Is4/mCluXc33vy8xTnJjTNqV/T8LKmkhr8Hq1da2aOvTfIh2CFeNt+GxFBP8AJFdFUbPWh+4FdXV7OtZOMR7mK9lBWNN+JBmMQ5cwmfH8DEFhTZUCRlE6CBq/ds/nBh9oYygeY1L9FnCUnBSN1t+w0l9bNomx1cllsOrL9OCTKtKOIqua6UVjP0dEvTyM7gp/3whbkAD0ScX3r6MLg+C2/XsMhCnJRn/5cVNHyJHiX6JKIFhhqnFeagm9BIgjfcJyNBTZiROBUk6Mp8CJRmT4NWU2MatV7n495DPk/wAbMJSRJOTBDItq0KR5s/nJN7LPW8AJWtYAoKQaDp+u4XShxgXhYcbHoxNTllCwETGQ8ag2jmDVsk8w/wCOp/C/hn+mWV/utpePH+D5wmF39NY6UakjUYR1Dn0YgRM5zQAAAMdfAA4AOAOArjkMNQ3vgm7UKtBR+m9QHFD5tpnDtpy+t2R20gK/OsmFtuDpaL5mVyiT5qdEVAvZci5ch5VoSGKbwlWTBr0RPoZT07av9lHfrXo6yLApWMugKpPM9SV1cDm65s/wkOHZBojoqiM+6GpMSj4FhtayNAUi5H3LfQBG2KWssFoSPuJdKyMLKtpuLi+e3jwFICUg7CSHsNVlYlKdizOTvKdq3KTsG8pQirsAG6vAB5FdhP490U4gfjxi+DedoqO4YftmKdKNulO26jiOv+2Ga/bftVNFXpHtVHrpLpRFJTpP3z77T469++fTx48e4LueE+NY6UKk7UniLP8A7rNf3X6//9k=</binary><binary id="image1" content-type="image/jpeg">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</binary></FictionBook>
\ No newline at end of file diff --git a/tests/writer.html b/tests/writer.html index c9ef6f1f1..ce3d7cae6 100644 --- a/tests/writer.html +++ b/tests/writer.html @@ -8,6 +8,7 @@ <meta name="author" content="Anonymous" /> <meta name="date" content="2006-07-17" /> <title>Pandoc Test Suite</title> + <style type="text/css">code{white-space: pre;}</style> </head> <body> <div id="header"> @@ -281,29 +282,34 @@ These should not be escaped: \$ \\ \> \[ \{</code></pre> <dl> <dt>apple</dt> <dd>red fruit -</dd><dd>computer +</dd> +<dd>computer </dd> <dt>orange</dt> <dd>orange fruit -</dd><dd>bank +</dd> +<dd>bank </dd> </dl> <p>Multiple definitions, loose:</p> <dl> <dt>apple</dt> <dd><p>red fruit</p> -</dd><dd><p>computer</p> +</dd> +<dd><p>computer</p> </dd> <dt>orange</dt> <dd><p>orange fruit</p> -</dd><dd><p>bank</p> +</dd> +<dd><p>bank</p> </dd> </dl> <p>Blank line after term, indented marker, alternate markers:</p> <dl> <dt>apple</dt> <dd><p>red fruit</p> -</dd><dd><p>computer</p> +</dd> +<dd><p>computer</p> </dd> <dt>orange</dt> <dd><p>orange fruit</p> @@ -518,10 +524,10 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'Email link'+'<\/'+'a'+'>') <p>Here’s an <a href="/script?foo=1&bar=2">inline link</a>.</p> <p>Here’s an <a href="/script?foo=1&bar=2">inline link in pointy braces</a>.</p> <h2 id="autolinks">Autolinks</h2> -<p>With an ampersand: <a href="http://example.com/?foo=1&bar=2"><code class="url">http://example.com/?foo=1&bar=2</code></a></p> +<p>With an ampersand: <a href="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</a></p> <ul> <li>In a list?</li> -<li><a href="http://example.com/"><code class="url">http://example.com/</code></a></li> +<li><a href="http://example.com/">http://example.com/</a></li> <li>It should.</li> </ul> <p>An e-mail address: <script type="text/javascript"> @@ -531,7 +537,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'<code>'+e+'</code>'+'<\/'+ // --> </script><noscript>nobody at nowhere dot net</noscript></p> <blockquote> -<p>Blockquoted: <a href="http://example.com/"><code class="url">http://example.com/</code></a></p> +<p>Blockquoted: <a href="http://example.com/">http://example.com/</a></p> </blockquote> <p>Auto-links should not occur here: <code><http://example.com/></code></p> <pre><code>or here: <http://example.com/></code></pre> diff --git a/tests/writer.latex b/tests/writer.latex index 3efc08277..b3faaaa98 100644 --- a/tests/writer.latex +++ b/tests/writer.latex @@ -6,6 +6,8 @@ \usepackage{fixltx2e} % provides \textsubscript % use microtype if available \IfFileExists{microtype.sty}{\usepackage{microtype}}{} +% use upquote if available, for straight quotes in verbatim environments +\IfFileExists{upquote.sty}{\usepackage{upquote}}{} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex \usepackage[utf8]{inputenc} \else % if luatex or xelatex @@ -17,13 +19,6 @@ \newcommand{\euro}{€} \fi \usepackage{fancyvrb} -% Redefine labelwidth for lists; otherwise, the enumerate package will cause -% markers to extend beyond the left margin. -\makeatletter\AtBeginDocument{% - \renewcommand{\@listi} - {\setlength{\labelwidth}{4em}} -}\makeatother -\usepackage{enumerate} \usepackage{graphicx} % We will generate all images so they have a width \maxwidth. This means % that they will get their normal width if they fit onto the page, but @@ -127,7 +122,9 @@ sub status { A list: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item item one \item @@ -182,6 +179,7 @@ These should not be escaped: \$ \\ \> \[ \{ Asterisks tight: \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item asterisk 1 \item @@ -204,6 +202,7 @@ Asterisks loose: Pluses tight: \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item Plus 1 \item @@ -226,6 +225,7 @@ Pluses loose: Minuses tight: \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item Minus 1 \item @@ -249,7 +249,9 @@ Minuses loose: Tight: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item First \item @@ -260,7 +262,9 @@ Tight: and: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item One \item @@ -271,7 +275,8 @@ and: Loose using tabs: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} \item First \item @@ -282,7 +287,8 @@ Loose using tabs: and using spaces: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} \item One \item @@ -293,7 +299,8 @@ and using spaces: Multiple paragraphs: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} \item Item 1, graf one. @@ -307,14 +314,17 @@ Multiple paragraphs: \subsection{Nested} \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item Tab \begin{itemize} + \itemsep1pt\parskip0pt\parsep0pt \item Tab \begin{itemize} + \itemsep1pt\parskip0pt\parsep0pt \item Tab \end{itemize} @@ -323,13 +333,16 @@ Multiple paragraphs: Here's another: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item First \item Second: \begin{itemize} + \itemsep1pt\parskip0pt\parsep0pt \item Fee \item @@ -343,13 +356,15 @@ Here's another: Same thing but with paragraphs: -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} \item First \item Second: \begin{itemize} + \itemsep1pt\parskip0pt\parsep0pt \item Fee \item @@ -379,7 +394,8 @@ Same thing but with paragraphs: \subsection{Fancy list markers} -\begin{enumerate}[(1)] +\begin{enumerate} +\def\labelenumi{(\arabic{enumi})} \setcounter{enumi}{1} \item begins with 2 @@ -388,14 +404,18 @@ Same thing but with paragraphs: with a continuation - \begin{enumerate}[i.] + \begin{enumerate} + \def\labelenumii{\roman{enumii}.} \setcounter{enumii}{3} + \itemsep1pt\parskip0pt\parsep0pt \item sublist with roman numerals, starting with 4 \item more items - \begin{enumerate}[(A)] + \begin{enumerate} + \def\labelenumiii{(\Alph{enumiii})} + \itemsep1pt\parskip0pt\parsep0pt \item a subsublist \item @@ -406,21 +426,29 @@ Same thing but with paragraphs: Nesting: -\begin{enumerate}[A.] +\begin{enumerate} +\def\labelenumi{\Alph{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item Upper Alpha - \begin{enumerate}[I.] + \begin{enumerate} + \def\labelenumii{\Roman{enumii}.} + \itemsep1pt\parskip0pt\parsep0pt \item Upper Roman. - \begin{enumerate}[(1)] + \begin{enumerate} + \def\labelenumiii{(\arabic{enumiii})} \setcounter{enumiii}{5} + \itemsep1pt\parskip0pt\parsep0pt \item Decimal start with 6 - \begin{enumerate}[a)] + \begin{enumerate} + \def\labelenumiv{\alph{enumiv})} \setcounter{enumiv}{2} + \itemsep1pt\parskip0pt\parsep0pt \item Lower alpha with paren \end{enumerate} @@ -431,12 +459,14 @@ Nesting: Autonumbering: \begin{enumerate} +\itemsep1pt\parskip0pt\parsep0pt \item Autonumber. \item More. \begin{enumerate} + \itemsep1pt\parskip0pt\parsep0pt \item Nested. \end{enumerate} @@ -455,6 +485,7 @@ B. Williams Tight using spaces: \begin{description} +\itemsep1pt\parskip0pt\parsep0pt \item[apple] red fruit \item[orange] @@ -466,6 +497,7 @@ yellow fruit Tight using tabs: \begin{description} +\itemsep1pt\parskip0pt\parsep0pt \item[apple] red fruit \item[orange] @@ -507,6 +539,7 @@ orange block quote Multiple definitions, tight: \begin{description} +\itemsep1pt\parskip0pt\parsep0pt \item[apple] red fruit @@ -540,7 +573,9 @@ computer \item[orange] orange fruit -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item sublist \item @@ -666,6 +701,7 @@ Ellipses\ldots{}and\ldots{}and\ldots{}. \section{LaTeX} \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item \cite[22-23]{smith.1899} \item @@ -688,6 +724,7 @@ Ellipses\ldots{}and\ldots{}and\ldots{}. These shouldn't be math: \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item To get the famous equation, write \texttt{\$e = mc\^{}2\$}. \item @@ -714,6 +751,7 @@ Cat & 1 \\ \hline Here is some unicode: \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item I hat: Î \item @@ -786,7 +824,7 @@ Just a \href{/url/}{URL}. \href{/url/}{URL and title} -\href{/url/with\_underscore}{with\_underscore} +\href{/url/with_underscore}{with\_underscore} \href{mailto:nobody@nowhere.net}{Email link} @@ -834,9 +872,10 @@ 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} +With an ampersand: \url{http://example.com/?foo=1\&bar=2} \begin{itemize} +\itemsep1pt\parskip0pt\parsep0pt \item In a list? \item @@ -845,8 +884,7 @@ With an ampersand: \url{http://example.com/?foo=1&bar=2} It should. \end{itemize} -An e-mail address: -\href{mailto:nobody@nowhere.net}{\texttt{nobody@nowhere.net}} +An e-mail address: \href{mailto:nobody@nowhere.net}{nobody@nowhere.net} \begin{quote} Blockquoted: \url{http://example.com/} @@ -900,7 +938,9 @@ note.\footnote{This is \emph{easier} to type. Inline notes may contain Notes can go in quotes.\footnote{In quote.} \end{quote} -\begin{enumerate}[1.] +\begin{enumerate} +\def\labelenumi{\arabic{enumi}.} +\itemsep1pt\parskip0pt\parsep0pt \item And in list items.\footnote{In list.} \end{enumerate} diff --git a/tests/writer.man b/tests/writer.man index 28bc0feb5..f0a6f348a 100644 --- a/tests/writer.man +++ b/tests/writer.man @@ -26,7 +26,7 @@ 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 +Because a hard\-wrapped line in the middle of a paragraph looked like a list item. .PP Here's one with a bullet. @@ -41,7 +41,7 @@ here. * * * * * .SH Block Quotes .PP -E-mail style: +E\-mail style: .RS .PP This is a block quote. @@ -87,7 +87,7 @@ Code: .IP .nf \f[C] -----\ (should\ be\ four\ hyphens) +\-\-\-\-\ (should\ be\ four\ hyphens) sub\ status\ { \ \ \ \ print\ "working"; @@ -489,7 +489,7 @@ Code block: .IP .nf \f[C] -<!--\ Comment\ --> +<!\-\-\ Comment\ \-\-> \f[] .fi .PP @@ -568,11 +568,11 @@ Ellipses\&...and\&...and\&.... .IP \[bu] 2 223 .IP \[bu] 2 -\f[I]p\f[]-Tree +\f[I]p\f[]\-Tree .IP \[bu] 2 Here's some display math: .RS -$\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}$ +$\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)\-f(x)}{h}$ .RE .IP \[bu] 2 Here's one that has a line break in it: @@ -637,7 +637,7 @@ Left paren: ( .PP Right paren: ) .PP -Greater-than: > +Greater\-than: > .PP Hash: # .PP @@ -647,7 +647,7 @@ Bang: ! .PP Plus: + .PP -Minus: - +Minus: \- .PP * * * * * .SH Links @@ -718,13 +718,13 @@ In a list? .IP \[bu] 2 It should. .PP -An e-mail address: <nobody@nowhere.net> +An e\-mail address: <nobody@nowhere.net> .RS .PP Blockquoted: <http://example.com/> .RE .PP -Auto-links should not occur here: \f[C]<http://example.com/>\f[] +Auto\-links should not occur here: \f[C]<http://example.com/>\f[] .IP .nf \f[C] diff --git a/tests/writer.mediawiki b/tests/writer.mediawiki index 9d89cd7bd..7eccc44e8 100644 --- a/tests/writer.mediawiki +++ b/tests/writer.mediawiki @@ -36,8 +36,7 @@ In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Beca Here’s one with a bullet. * criminey. -There should be a hard line break<br /> -here. +There should be a hard line break<br />here. ----- @@ -433,7 +432,7 @@ So is '''''this''''' word. So is '''''this''''' word. -This is code: <tt>></tt>, <tt>$</tt>, <tt>\</tt>, <tt>\$</tt>, <tt><html></tt>. +This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code><html></code>. <s>This is ''strikeout''.</s> @@ -456,7 +455,7 @@ These should not be superscripts or subscripts, because of the unescaped spaces: ‘He said, “I want to go.”’ Were you alive in the 70’s? -Here is some quoted ‘<tt>code</tt>’ and a “[http://example.com/?foo=1&bar=2 quoted link]”. +Here is some quoted ‘<code>code</code>’ and a “[http://example.com/?foo=1&bar=2 quoted link]”. Some dashes: one—two — three—four — five. @@ -480,10 +479,10 @@ Ellipses…and…and…. These shouldn’t be math: -* To get the famous equation, write <tt>$e = mc^2$</tt>. +* 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 <tt>$</tt>: $73 ''this should be emphasized'' 23$. +* Escaped <code>$</code>: $73 ''this should be emphasized'' 23$. Here’s a LaTeX table: @@ -611,11 +610,11 @@ With an ampersand: http://example.com/?foo=1&bar=2 * http://example.com/ * It should. -An e-mail address: [mailto:nobody@nowhere.net <tt>nobody@nowhere.net</tt>] +An e-mail address: [mailto:nobody@nowhere.net nobody@nowhere.net] <blockquote>Blockquoted: http://example.com/ </blockquote> -Auto-links should not occur here: <tt><http://example.com/></tt> +Auto-links should not occur here: <code><http://example.com/></code> <pre>or here: <http://example.com/></pre> @@ -641,7 +640,7 @@ Subsequent blocks are indented to show that they belong to the footnote (as with <pre> { <code> }</pre> If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. -</ref> This should ''not'' be a footnote reference, because it contains a space.[^my note] Here is an inline note.<ref>This is ''easier'' to type. Inline notes may contain [http://google.com links] and <tt>]</tt> verbatim characters, as well as [bracketed text]. +</ref> This should ''not'' be a footnote reference, because it contains a space.[^my note] Here is an inline note.<ref>This is ''easier'' to type. Inline notes may contain [http://google.com links] and <code>]</code> verbatim characters, as well as [bracketed text]. </ref> <blockquote>Notes can go in quotes.<ref>In quote. diff --git a/tests/writer.native b/tests/writer.native index 691c4959a..90727a660 100644 --- a/tests/writer.native +++ b/tests/writer.native @@ -1,172 +1,172 @@ -Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [Str "July",Space,Str "17",Str ",",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",Str ".",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",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."] +Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [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",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,HorizontalRule -,Header 1 [Str "Headers"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")] -,Header 3 [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 1 [Str "Level",Space,Str "1"] -,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Emph [Str "emphasis"]] -,Header 3 [Str "Level",Space,Str "3"] +,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 [Str "Level",Space,Str "2"] +,Header 2 ("level-2",[],[]) [Str "Level",Space,Str "2"] ,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"] ,HorizontalRule -,Header 1 [Str "Paragraphs"] -,Para [Str "Here\8217s",Space,Str "a",Space,Str "regular",Space,Str "paragraph",Str "."] -,Para [Str "In",Space,Str "Markdown",Space,Str "1",Str ".",Str "0",Str ".",Str "0",Space,Str "and",Space,Str "earlier",Str ".",Space,Str "Version",Space,Str "8",Str ".",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item",Str ".",Space,Str "Because",Space,Str "a",Space,Str "hard",Str "-",Str "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",Str "."] -,Para [Str "Here\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet",Str ".",Space,Str "*",Space,Str "criminey",Str "."] -,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "hard",Space,Str "line",Space,Str "break",LineBreak,Str "here",Str "."] +,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",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\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",Str "-",Str "mail",Space,Str "style",Str ":"] +,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",Str ".",Space,Str "It",Space,Str "is",Space,Str "pretty",Space,Str "short",Str "."]] + [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",Str ":"] + [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",Str ":"] + ,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",Str ":"] + ,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",Str ":",Space,Str "2",Space,Str ">",Space,Str "1",Str "."] -,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph",Str "."] +,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 -,Header 1 [Str "Code",Space,Str "Blocks"] -,Para [Str "Code",Str ":"] +,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",Str ":"] +,Para [Str "And:"] ,CodeBlock ("",[],[]) " this code block is indented by two tabs\n\nThese should not be escaped: \\$ \\\\ \\> \\[ \\{" ,HorizontalRule -,Header 1 [Str "Lists"] -,Header 2 [Str "Unordered"] -,Para [Str "Asterisks",Space,Str "tight",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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 [Str "Ordered"] -,Para [Str "Tight",Str ":"] +,Header 2 ("ordered",[],[]) [Str "Ordered"] +,Para [Str "Tight:"] ,OrderedList (1,Decimal,Period) [[Plain [Str "First"]] ,[Plain [Str "Second"]] ,[Plain [Str "Third"]]] -,Para [Str "and",Str ":"] +,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",Str ":"] +,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",Str ":"] +,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",Str ":"] +,Para [Str "Multiple",Space,Str "paragraphs:"] ,OrderedList (1,Decimal,Period) - [[Para [Str "Item",Space,Str "1",Str ",",Space,Str "graf",Space,Str "one",Str "."] - ,Para [Str "Item",Space,Str "1",Str ".",Space,Str "graf",Space,Str "two",Str ".",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",Str "."]] - ,[Para [Str "Item",Space,Str "2",Str "."]] - ,[Para [Str "Item",Space,Str "3",Str "."]]] -,Header 2 [Str "Nested"] + [[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."]]] +,Header 2 ("nested",[],[]) [Str "Nested"] ,BulletList [[Plain [Str "Tab"] ,BulletList [[Plain [Str "Tab"] ,BulletList [[Plain [Str "Tab"]]]]]]] -,Para [Str "Here\8217s",Space,Str "another",Str ":"] +,Para [Str "Here\8217s",Space,Str "another:"] ,OrderedList (1,Decimal,Period) [[Plain [Str "First"]] - ,[Plain [Str "Second",Str ":"] + ,[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",Str ":"] +,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",Str ":"] + ,[Para [Str "Second:"] ,BulletList [[Plain [Str "Fee"]] ,[Plain [Str "Fie"]] ,[Plain [Str "Foe"]]]] ,[Para [Str "Third"]]] -,Header 2 [Str "Tabs",Space,Str "and",Space,Str "spaces"] +,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 [Str "Fancy",Space,Str "list",Space,Str "markers"] +,Header 2 ("fancy-list-markers",[],[]) [Str "Fancy",Space,Str "list",Space,Str "markers"] ,OrderedList (2,Decimal,TwoParens) [[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,Period) - [[Plain [Str "sublist",Space,Str "with",Space,Str "roman",Space,Str "numerals",Str ",",Space,Str "starting",Space,Str "with",Space,Str "4"]] + [[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",Str ":"] +,Para [Str "Nesting:"] ,OrderedList (1,UpperAlpha,Period) [[Plain [Str "Upper",Space,Str "Alpha"] ,OrderedList (1,UpperRoman,Period) - [[Plain [Str "Upper",Space,Str "Roman",Str "."] + [[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",Str ":"] +,Para [Str "Autonumbering:"] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Autonumber",Str "."]] - ,[Plain [Str "More",Str "."] + [[Plain [Str "Autonumber."]] + ,[Plain [Str "More."] ,OrderedList (1,DefaultStyle,DefaultDelim) - [[Plain [Str "Nested",Str "."]]]]] -,Para [Str "Should",Space,Str "not",Space,Str "be",Space,Str "a",Space,Str "list",Space,Str "item",Str ":"] -,Para [Str "M.A.\160",Str "2007"] -,Para [Str "B",Str ".",Space,Str "Williams"] + [[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 [Str "Definition",Space,Str "Lists"] -,Para [Str "Tight",Space,Str "using",Space,Str "spaces",Str ":"] +,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"]]]) @@ -174,7 +174,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Plain [Str "orange",Space,Str "fruit"]]]) ,([Str "banana"], [[Plain [Str "yellow",Space,Str "fruit"]]])] -,Para [Str "Tight",Space,Str "using",Space,Str "tabs",Str ":"] +,Para [Str "Tight",Space,Str "using",Space,Str "tabs:"] ,DefinitionList [([Str "apple"], [[Plain [Str "red",Space,Str "fruit"]]]) @@ -182,7 +182,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[Plain [Str "orange",Space,Str "fruit"]]]) ,([Str "banana"], [[Plain [Str "yellow",Space,Str "fruit"]]])] -,Para [Str "Loose",Str ":"] +,Para [Str "Loose:"] ,DefinitionList [([Str "apple"], [[Para [Str "red",Space,Str "fruit"]]]) @@ -190,17 +190,17 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA [[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",Str ":"] +,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",Str ",",Space,Str "crisp",Str ",",Space,Str "pleasant",Space,Str "to",Space,Str "taste"]]]) + ,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",Str ",",Space,Str "tight",Str ":"] +,Para [Str "Multiple",Space,Str "definitions,",Space,Str "tight:"] ,DefinitionList [([Str "apple"], [[Plain [Str "red",Space,Str "fruit"]] @@ -208,7 +208,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,([Str "orange"], [[Plain [Str "orange",Space,Str "fruit"]] ,[Plain [Str "bank"]]])] -,Para [Str "Multiple",Space,Str "definitions",Str ",",Space,Str "loose",Str ":"] +,Para [Str "Multiple",Space,Str "definitions,",Space,Str "loose:"] ,DefinitionList [([Str "apple"], [[Para [Str "red",Space,Str "fruit"]] @@ -216,7 +216,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,([Str "orange"], [[Para [Str "orange",Space,Str "fruit"]] ,[Para [Str "bank"]]])] -,Para [Str "Blank",Space,Str "line",Space,Str "after",Space,Str "term",Str ",",Space,Str "indented",Space,Str "marker",Str ",",Space,Str "alternate",Space,Str "markers",Str ":"] +,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"]] @@ -226,171 +226,171 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA ,OrderedList (1,Decimal,Period) [[Plain [Str "sublist"]] ,[Plain [Str "sublist"]]]]])] -,Header 1 [Str "HTML",Space,Str "Blocks"] -,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line",Str ":"] +,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 "html" "<div>" ,Plain [Str "foo"] ,RawBlock "html" "</div>\n" -,Para [Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation",Str ":"] +,Para [Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation:"] ,RawBlock "html" "<div>\n<div>\n<div>" ,Plain [Str "foo"] ,RawBlock "html" "</div>\n</div>\n<div>" ,Plain [Str "bar"] ,RawBlock "html" "</div>\n</div>\n" -,Para [Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table",Str ":"] +,Para [Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table:"] ,RawBlock "html" "<table>\n<tr>\n<td>" ,Plain [Str "This",Space,Str "is",Space,Emph [Str "emphasized"]] ,RawBlock "html" "</td>\n<td>" ,Plain [Str "And",Space,Str "this",Space,Str "is",Space,Strong [Str "strong"]] ,RawBlock "html" "</td>\n</tr>\n</table>\n\n<script type=\"text/javascript\">document.write('This *should not* be interpreted as markdown');</script>\n" -,Para [Str "Here\8217s",Space,Str "a",Space,Str "simple",Space,Str "block",Str ":"] +,Para [Str "Here\8217s",Space,Str "a",Space,Str "simple",Space,Str "block:"] ,RawBlock "html" "<div>\n " ,Plain [Str "foo"] ,RawBlock "html" "</div>\n" -,Para [Str "This",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "code",Space,Str "block",Str ",",Space,Str "though",Str ":"] +,Para [Str "This",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "code",Space,Str "block,",Space,Str "though:"] ,CodeBlock ("",[],[]) "<div>\n foo\n</div>" -,Para [Str "As",Space,Str "should",Space,Str "this",Str ":"] +,Para [Str "As",Space,Str "should",Space,Str "this:"] ,CodeBlock ("",[],[]) "<div>foo</div>" -,Para [Str "Now",Str ",",Space,Str "nested",Str ":"] +,Para [Str "Now,",Space,Str "nested:"] ,RawBlock "html" "<div>\n <div>\n <div>\n " ,Plain [Str "foo"] ,RawBlock "html" "</div>\n </div>\n</div>\n" -,Para [Str "This",Space,Str "should",Space,Str "just",Space,Str "be",Space,Str "an",Space,Str "HTML",Space,Str "comment",Str ":"] +,Para [Str "This",Space,Str "should",Space,Str "just",Space,Str "be",Space,Str "an",Space,Str "HTML",Space,Str "comment:"] ,RawBlock "html" "<!-- Comment -->\n" -,Para [Str "Multiline",Str ":"] +,Para [Str "Multiline:"] ,RawBlock "html" "<!--\nBlah\nBlah\n-->\n\n<!--\n This is another comment.\n-->\n" -,Para [Str "Code",Space,Str "block",Str ":"] +,Para [Str "Code",Space,Str "block:"] ,CodeBlock ("",[],[]) "<!-- Comment -->" -,Para [Str "Just",Space,Str "plain",Space,Str "comment",Str ",",Space,Str "with",Space,Str "trailing",Space,Str "spaces",Space,Str "on",Space,Str "the",Space,Str "line",Str ":"] +,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 "html" "<!-- foo --> \n" -,Para [Str "Code",Str ":"] +,Para [Str "Code:"] ,CodeBlock ("",[],[]) "<hr />" -,Para [Str "Hr\8217s",Str ":"] +,Para [Str "Hr\8217s:"] ,RawBlock "html" "<hr>\n\n<hr />\n\n<hr />\n\n<hr> \n\n<hr /> \n\n<hr /> \n\n<hr class=\"foo\" id=\"bar\" />\n\n<hr class=\"foo\" id=\"bar\" />\n\n<hr class=\"foo\" id=\"bar\">\n" ,HorizontalRule -,Header 1 [Str "Inline",Space,Str "Markup"] +,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",Str "."]]] -,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Str "."] -,Para [Strong [Emph [Str "This",Space,Str "is",Space,Str "strong",Space,Str "and",Space,Str "em",Str "."]]] -,Para [Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Str "."] -,Para [Str "This",Space,Str "is",Space,Str "code",Str ":",Space,Code ("",[],[]) ">",Str ",",Space,Code ("",[],[]) "$",Str ",",Space,Code ("",[],[]) "\\",Str ",",Space,Code ("",[],[]) "\\$",Str ",",Space,Code ("",[],[]) "<html>",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 ("",[],[]) "<html>",Str "."] ,Para [Strikeout [Str "This",Space,Str "is",Space,Emph [Str "strikeout"],Str "."]] -,Para [Str "Superscripts",Str ":",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Superscript [Emph [Str "hello"]],Space,Str "a",Superscript [Str "hello",Str "\160",Str "there"],Str "."] -,Para [Str "Subscripts",Str ":",Space,Str "H",Subscript [Str "2"],Str "O",Str ",",Space,Str "H",Subscript [Str "23"],Str "O",Str ",",Space,Str "H",Subscript [Str "many",Str "\160",Str "of",Str "\160",Str "them"],Str "O",Str "."] -,Para [Str "These",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "superscripts",Space,Str "or",Space,Str "subscripts",Str ",",Space,Str "because",Space,Str "of",Space,Str "the",Space,Str "unescaped",Space,Str "spaces",Str ":",Space,Str "a",Str "^",Str "b",Space,Str "c",Str "^",Str "d",Str ",",Space,Str "a",Str "~",Str "b",Space,Str "c",Str "~",Str "d",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:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a~b",Space,Str "c~d."] ,HorizontalRule -,Header 1 [Str "Smart",Space,Str "quotes",Str ",",Space,Str "ellipses",Str ",",Space,Str "dashes"] -,Para [Quoted DoubleQuote [Str "Hello",Str ","],Space,Str "said",Space,Str "the",Space,Str "spider",Str ".",Space,Quoted DoubleQuote [Quoted SingleQuote [Str "Shelob"],Space,Str "is",Space,Str "my",Space,Str "name",Str "."]] -,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",Str "."] -,Para [Quoted SingleQuote [Str "Oak",Str ","],Space,Quoted SingleQuote [Str "elm",Str ","],Space,Str "and",Space,Quoted SingleQuote [Str "beech"],Space,Str "are",Space,Str "names",Space,Str "of",Space,Str "trees",Str ".",Space,Str "So",Space,Str "is",Space,Quoted SingleQuote [Str "pine",Str "."]] -,Para [Quoted SingleQuote [Str "He",Space,Str "said",Str ",",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go",Str "."]],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70\8217s",Str "?"] +,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.",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",Space,Str "link"] ("http://example.com/?foo=1&bar=2","")],Str "."] -,Para [Str "Some",Space,Str "dashes",Str ":",Space,Str "one",Str "\8212",Str "two",Space,Str "\8212",Space,Str "three",Str "\8212",Str "four",Space,Str "\8212",Space,Str "five",Str "."] -,Para [Str "Dashes",Space,Str "between",Space,Str "numbers",Str ":",Space,Str "5",Str "\8211",Str "7",Str ",",Space,Str "255",Str "\8211",Str "66",Str ",",Space,Str "1987",Str "\8211",Str "1999",Str "."] -,Para [Str "Ellipses",Str "\8230",Str "and",Str "\8230",Str "and",Str "\8230",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 [Str "LaTeX"] +,Header 1 ("latex",[],[]) [Str "LaTeX"] ,BulletList [[Plain [RawInline "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 "-",Str "Tree"]] - ,[Plain [Str "Here\8217s",Space,Str "some",Space,Str "display",Space,Str "math",Str ":",Space,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",Str ":",Space,Math InlineMath "\\alpha + \\omega \\times x^2",Str "."]]] -,Para [Str "These",Space,Str "shouldn\8217t",Space,Str "be",Space,Str "math",Str ":"] + ,[Plain [Math InlineMath "p",Str "-Tree"]] + ,[Plain [Str "Here\8217s",Space,Str "some",Space,Str "display",Space,Str "math:",Space,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",Str ",",Space,Str "write",Space,Code ("",[],[]) "$e = mc^2$",Str "."]] - ,[Plain [Str "$",Str "22",Str ",",Str "000",Space,Str "is",Space,Str "a",Space,Emph [Str "lot"],Space,Str "of",Space,Str "money",Str ".",Space,Str "So",Space,Str "is",Space,Str "$",Str "34",Str ",",Str "000",Str ".",Space,Str "(",Str "It",Space,Str "worked",Space,Str "if",Space,Quoted DoubleQuote [Str "lot"],Space,Str "is",Space,Str "emphasized",Str ".",Str ")"]] - ,[Plain [Str "Shoes",Space,Str "(",Str "$",Str "20",Str ")",Space,Str "and",Space,Str "socks",Space,Str "(",Str "$",Str "5",Str ")",Str "."]] - ,[Plain [Str "Escaped",Space,Code ("",[],[]) "$",Str ":",Space,Str "$",Str "73",Space,Emph [Str "this",Space,Str "should",Space,Str "be",Space,Str "emphasized"],Space,Str "23",Str "$",Str "."]]] -,Para [Str "Here\8217s",Space,Str "a",Space,Str "LaTeX",Space,Str "table",Str ":"] + [[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",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 "latex" "\\begin{tabular}{|l|l|}\\hline\nAnimal & Number \\\\ \\hline\nDog & 2 \\\\\nCat & 1 \\\\ \\hline\n\\end{tabular}" ,HorizontalRule -,Header 1 [Str "Special",Space,Str "Characters"] -,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "unicode",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",Str ":",Space,Str "\206"]] - ,[Plain [Str "o",Space,Str "umlaut",Str ":",Space,Str "\246"]] - ,[Plain [Str "section",Str ":",Space,Str "\167"]] - ,[Plain [Str "set",Space,Str "membership",Str ":",Space,Str "\8712"]] - ,[Plain [Str "copyright",Str ":",Space,Str "\169"]]] -,Para [Str "AT",Str "&",Str "T",Space,Str "has",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "their",Space,Str "name",Str "."] -,Para [Str "AT",Str "&",Str "T",Space,Str "is",Space,Str "another",Space,Str "way",Space,Str "to",Space,Str "write",Space,Str "it",Str "."] -,Para [Str "This",Space,Str "&",Space,Str "that",Str "."] -,Para [Str "4",Space,Str "<",Space,Str "5",Str "."] -,Para [Str "6",Space,Str ">",Space,Str "5",Str "."] -,Para [Str "Backslash",Str ":",Space,Str "\\"] -,Para [Str "Backtick",Str ":",Space,Str "`"] -,Para [Str "Asterisk",Str ":",Space,Str "*"] -,Para [Str "Underscore",Str ":",Space,Str "_"] -,Para [Str "Left",Space,Str "brace",Str ":",Space,Str "{"] -,Para [Str "Right",Space,Str "brace",Str ":",Space,Str "}"] -,Para [Str "Left",Space,Str "bracket",Str ":",Space,Str "["] -,Para [Str "Right",Space,Str "bracket",Str ":",Space,Str "]"] -,Para [Str "Left",Space,Str "paren",Str ":",Space,Str "("] -,Para [Str "Right",Space,Str "paren",Str ":",Space,Str ")"] -,Para [Str "Greater",Str "-",Str "than",Str ":",Space,Str ">"] -,Para [Str "Hash",Str ":",Space,Str "#"] -,Para [Str "Period",Str ":",Space,Str "."] -,Para [Str "Bang",Str ":",Space,Str "!"] -,Para [Str "Plus",Str ":",Space,Str "+"] -,Para [Str "Minus",Str ":",Space,Str "-"] + [[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 [Str "Links"] -,Header 2 [Str "Explicit"] +,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",Str "_",Str "underscore"] ("/url/with_underscore","")] +,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 [Str "Reference"] +,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 "[",Str "brackets",Str "]"] ("/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",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 "[",Str "not",Str "]",Str "[",Str "]",Space,Str "be",Space,Str "a",Space,Str "link",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 [Str "With",Space,Str "ampersands"] +,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",Str ":",Space,Link [Str "AT",Str "&",Str "T"] ("http://att.com/","AT&T"),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 [Str "Autolinks"] -,Para [Str "With",Space,Str "an",Space,Str "ampersand",Str ":",Space,Link [Code ("",["url"],[]) "http://example.com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")] +,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",Str "?"]] - ,[Plain [Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] - ,[Plain [Str "It",Space,Str "should",Str "."]]] -,Para [Str "An",Space,Str "e",Str "-",Str "mail",Space,Str "address",Str ":",Space,Link [Code ("",["url"],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] + [[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,Link [Str "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")] ,BlockQuote - [Para [Str "Blockquoted",Str ":",Space,Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]] -,Para [Str "Auto",Str "-",Str "links",Space,Str "should",Space,Str "not",Space,Str "occur",Space,Str "here",Str ":",Space,Code ("",[],[]) "<http://example.com/>"] + [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 ("",[],[]) "<http://example.com/>"] ,CodeBlock ("",[],[]) "or here: <http://example.com/>" ,HorizontalRule -,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 "(",Str "1902",Str ")",Str ":"] -,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",Str "."] +,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 "lalune"] ("lalune.jpg","fig: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 [Str "Footnotes"] -,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "footnote",Space,Str "reference",Str ",",Note [Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "footnote",Str ".",Space,Str "It",Space,Str "can",Space,Str "go",Space,Str "anywhere",Space,Str "after",Space,Str "the",Space,Str "footnote",Space,Str "reference",Str ".",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",Str "."]],Space,Str "and",Space,Str "another",Str ".",Note [Para [Str "Here\8217s",Space,Str "the",Space,Str "long",Space,Str "note",Str ".",Space,Str "This",Space,Str "one",Space,Str "contains",Space,Str "multiple",Space,Str "blocks",Str "."],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 "(",Str "as",Space,Str "with",Space,Str "list",Space,Str "items",Str ")",Str "."],CodeBlock ("",[],[]) " { <code> }",Para [Str "If",Space,Str "you",Space,Str "want",Str ",",Space,Str "you",Space,Str "can",Space,Str "indent",Space,Str "every",Space,Str "line",Str ",",Space,Str "but",Space,Str "you",Space,Str "can",Space,Str "also",Space,Str "be",Space,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",Str "."]],Space,Str "This",Space,Str "should",Space,Emph [Str "not"],Space,Str "be",Space,Str "a",Space,Str "footnote",Space,Str "reference",Str ",",Space,Str "because",Space,Str "it",Space,Str "contains",Space,Str "a",Space,Str "space",Str ".",Str "[",Str "^",Str "my",Space,Str "note",Str "]",Space,Str "Here",Space,Str "is",Space,Str "an",Space,Str "inline",Space,Str "note",Str ".",Note [Para [Str "This",Space,Str "is",Space,Emph [Str "easier"],Space,Str "to",Space,Str "type",Str ".",Space,Str "Inline",Space,Str "notes",Space,Str "may",Space,Str "contain",Space,Link [Str "links"] ("http://google.com",""),Space,Str "and",Space,Code ("",[],[]) "]",Space,Str "verbatim",Space,Str "characters",Str ",",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "[",Str "bracketed",Space,Str "text",Str "]",Str "."]]] +,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.",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",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",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",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",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,",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",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",Space,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",Space,Str "[bracketed",Space,Str "text]."]]] ,BlockQuote - [Para [Str "Notes",Space,Str "can",Space,Str "go",Space,Str "in",Space,Str "quotes",Str ".",Note [Para [Str "In",Space,Str "quote",Str "."]]]] + [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",Str ".",Note [Para [Str "In",Space,Str "list",Str "."]]]]] -,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",Str ",",Space,Str "as",Space,Str "it",Space,Str "is",Space,Str "not",Space,Str "indented",Str "."]] + [[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."]] diff --git a/tests/writer.opendocument b/tests/writer.opendocument index 587c16502..8727373a0 100644 --- a/tests/writer.opendocument +++ b/tests/writer.opendocument @@ -665,27 +665,27 @@ <style:style style:name="T35" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" /></style:style> <style:style style:name="T36" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" /></style:style> <style:style style:name="T37" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" /></style:style> - <style:style style:name="T38" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" /></style:style> - <style:style style:name="T39" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" /></style:style> + <style:style style:name="T38" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> + <style:style style:name="T39" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> <style:style style:name="T40" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> <style:style style:name="T41" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> - <style:style style:name="T42" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> + <style:style style:name="T42" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" style:text-line-through-style="solid" /></style:style> <style:style style:name="T43" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> - <style:style style:name="T44" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" style:text-line-through-style="solid" /></style:style> - <style:style style:name="T45" style:family="text"><style:text-properties style:text-line-through-style="solid" /></style:style> + <style:style style:name="T44" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> + <style:style style:name="T45" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" style:text-position="super 58%" /></style:style> <style:style style:name="T46" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> - <style:style style:name="T47" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" style:text-position="super 58%" /></style:style> - <style:style style:name="T48" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> - <style:style style:name="T49" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> - <style:style style:name="T50" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> - <style:style style:name="T51" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T52" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T53" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T54" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T55" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T56" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T57" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> - <style:style style:name="T58" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T47" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> + <style:style style:name="T48" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> + <style:style style:name="T49" style:family="text"><style:text-properties style:text-position="sub 58%" /></style:style> + <style:style style:name="T50" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T51" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T52" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T53" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T54" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T55" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T56" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T57" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> + <style:style style:name="T58" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> <style:style style:name="T59" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T60" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T61" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> @@ -693,17 +693,9 @@ <style:style style:name="T63" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T64" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T65" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T66" style:family="text"><style:text-properties style:text-position="super 58%" /></style:style> + <style:style style:name="T66" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T67" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="T68" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T69" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T70" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T71" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T72" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T73" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T74" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T75" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> - <style:style style:name="T76" style:family="text"><style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic" /></style:style> <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Quotations"> <style:paragraph-properties fo:margin-left="0.5in" fo:margin-right="0in" fo:text-indent="0in" style:auto-text-indent="false" /> </style:style> @@ -1342,33 +1334,33 @@ Markup</text:h> </text:span><text:span text:style-name="T20">is</text:span><text:span text:style-name="T21"> </text:span><text:span text:style-name="T22">strong</text:span><text:span text:style-name="T23"> </text:span><text:span text:style-name="T24">and</text:span><text:span text:style-name="T25"> -</text:span><text:span text:style-name="T26">em</text:span><text:span text:style-name="T27">.</text:span></text:p> +</text:span><text:span text:style-name="T26">em.</text:span></text:p> <text:p text:style-name="Text_20_body">So is -<text:span text:style-name="T28">this</text:span> word.</text:p> -<text:p text:style-name="Text_20_body"><text:span text:style-name="T29">This</text:span><text:span text:style-name="T30"> -</text:span><text:span text:style-name="T31">is</text:span><text:span text:style-name="T32"> -</text:span><text:span text:style-name="T33">strong</text:span><text:span text:style-name="T34"> -</text:span><text:span text:style-name="T35">and</text:span><text:span text:style-name="T36"> -</text:span><text:span text:style-name="T37">em</text:span><text:span text:style-name="T38">.</text:span></text:p> +<text:span text:style-name="T27">this</text:span> word.</text:p> +<text:p text:style-name="Text_20_body"><text:span text:style-name="T28">This</text:span><text:span text:style-name="T29"> +</text:span><text:span text:style-name="T30">is</text:span><text:span text:style-name="T31"> +</text:span><text:span text:style-name="T32">strong</text:span><text:span text:style-name="T33"> +</text:span><text:span text:style-name="T34">and</text:span><text:span text:style-name="T35"> +</text:span><text:span text:style-name="T36">em.</text:span></text:p> <text:p text:style-name="Text_20_body">So is -<text:span text:style-name="T39">this</text:span> word.</text:p> +<text:span text:style-name="T37">this</text:span> word.</text:p> <text:p text:style-name="Text_20_body">This is code: <text:span text:style-name="Teletype">></text:span>, <text:span text:style-name="Teletype">$</text:span>, <text:span text:style-name="Teletype">\</text:span>, <text:span text:style-name="Teletype">\$</text:span>, <text:span text:style-name="Teletype"><html></text:span>.</text:p> -<text:p text:style-name="Text_20_body"><text:span text:style-name="T40">This</text:span><text:span text:style-name="T41"> -</text:span><text:span text:style-name="T42">is</text:span><text:span text:style-name="T43"> -</text:span><text:span text:style-name="T44">strikeout</text:span><text:span text:style-name="T45">.</text:span></text:p> +<text:p text:style-name="Text_20_body"><text:span text:style-name="T38">This</text:span><text:span text:style-name="T39"> +</text:span><text:span text:style-name="T40">is</text:span><text:span text:style-name="T41"> +</text:span><text:span text:style-name="T42">strikeout</text:span><text:span text:style-name="T43">.</text:span></text:p> <text:p text:style-name="Text_20_body">Superscripts: -a<text:span text:style-name="T46">bc</text:span>d -a<text:span text:style-name="T47">hello</text:span> -a<text:span text:style-name="T48">hello</text:span><text:span text:style-name="T49"> </text:span><text:span text:style-name="T50">there</text:span>.</text:p> +a<text:span text:style-name="T44">bc</text:span>d +a<text:span text:style-name="T45">hello</text:span> +a<text:span text:style-name="T46">hello there</text:span>.</text:p> <text:p text:style-name="Text_20_body">Subscripts: -H<text:span text:style-name="T51">2</text:span>O, -H<text:span text:style-name="T52">23</text:span>O, -H<text:span text:style-name="T53">many</text:span><text:span text:style-name="T54"> </text:span><text:span text:style-name="T55">of</text:span><text:span text:style-name="T56"> </text:span><text:span text:style-name="T57">them</text:span>O.</text:p> +H<text:span text:style-name="T47">2</text:span>O, +H<text:span text:style-name="T48">23</text:span>O, +H<text:span text:style-name="T49">many of them</text:span>O.</text:p> <text:p text:style-name="Text_20_body">These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.</text:p> <text:p text:style-name="Horizontal_20_Line" /> @@ -1400,16 +1392,16 @@ five.</text:p> <text:p text:style-name="P51">2 + 2 = 4</text:p> </text:list-item> <text:list-item> - <text:p text:style-name="P51"><text:span text:style-name="T58">x</text:span> ∈ <text:span text:style-name="T59">y</text:span></text:p> + <text:p text:style-name="P51"><text:span text:style-name="T50">x</text:span> ∈ <text:span text:style-name="T51">y</text:span></text:p> </text:list-item> <text:list-item> - <text:p text:style-name="P51"><text:span text:style-name="T60">α</text:span> ∧ <text:span text:style-name="T61">ω</text:span></text:p> + <text:p text:style-name="P51"><text:span text:style-name="T52">α</text:span> ∧ <text:span text:style-name="T53">ω</text:span></text:p> </text:list-item> <text:list-item> <text:p text:style-name="P51">223</text:p> </text:list-item> <text:list-item> - <text:p text:style-name="P51"><text:span text:style-name="T62">p</text:span>-Tree</text:p> + <text:p text:style-name="P51"><text:span text:style-name="T54">p</text:span>-Tree</text:p> </text:list-item> <text:list-item> <text:p text:style-name="P51">Here’s some display math: @@ -1417,7 +1409,7 @@ five.</text:p> </text:list-item> <text:list-item> <text:p text:style-name="P51">Here’s one that has a line break in it: - <text:span text:style-name="T63">α</text:span> + <text:span text:style-name="T64">ω</text:span> × <text:span text:style-name="T65">x</text:span><text:span text:style-name="T66">2</text:span>.</text:p> + <text:span text:style-name="T55">α</text:span> + <text:span text:style-name="T56">ω</text:span> × <text:span text:style-name="T57">x</text:span><text:span text:style-name="T58">2</text:span>.</text:p> </text:list-item> </text:list> <text:p text:style-name="First_20_paragraph">These shouldn’t be math:</text:p> @@ -1428,7 +1420,7 @@ five.</text:p> </text:list-item> <text:list-item> <text:p text:style-name="P52">$22,000 is a - <text:span text:style-name="T67">lot</text:span> of money. So is $34,000. + <text:span text:style-name="T59">lot</text:span> of money. So is $34,000. (It worked if “lot” is emphasized.)</text:p> </text:list-item> <text:list-item> @@ -1437,10 +1429,10 @@ five.</text:p> <text:list-item> <text:p text:style-name="P52">Escaped <text:span text:style-name="Teletype">$</text:span>: $73 - <text:span text:style-name="T68">this</text:span><text:span text:style-name="T69"> - </text:span><text:span text:style-name="T70">should</text:span><text:span text:style-name="T71"> - </text:span><text:span text:style-name="T72">be</text:span><text:span text:style-name="T73"> - </text:span><text:span text:style-name="T74">emphasized</text:span> + <text:span text:style-name="T60">this</text:span><text:span text:style-name="T61"> + </text:span><text:span text:style-name="T62">should</text:span><text:span text:style-name="T63"> + </text:span><text:span text:style-name="T64">be</text:span><text:span text:style-name="T65"> + </text:span><text:span text:style-name="T66">emphasized</text:span> 23$.</text:p> </text:list-item> </text:list> @@ -1548,22 +1540,22 @@ link</text:span></text:a>.</text:p> link in pointy braces</text:span></text:a>.</text:p> <text:h text:style-name="Heading_20_2" text:outline-level="2">Autolinks</text:h> <text:p text:style-name="First_20_paragraph">With an ampersand: -<text:a xlink:type="simple" xlink:href="http://example.com/?foo=1&bar=2" office:name=""><text:span text:style-name="Definition"><text:span text:style-name="Teletype">http://example.com/?foo=1&bar=2</text:span></text:span></text:a></text:p> +<text:a xlink:type="simple" xlink:href="http://example.com/?foo=1&bar=2" office:name=""><text:span text:style-name="Definition">http://example.com/?foo=1&bar=2</text:span></text:a></text:p> <text:list text:style-name="L29"> <text:list-item> <text:p text:style-name="P55">In a list?</text:p> </text:list-item> <text:list-item> - <text:p text:style-name="P55"><text:a xlink:type="simple" xlink:href="http://example.com/" office:name=""><text:span text:style-name="Definition"><text:span text:style-name="Teletype">http://example.com/</text:span></text:span></text:a></text:p> + <text:p text:style-name="P55"><text:a xlink:type="simple" xlink:href="http://example.com/" office:name=""><text:span text:style-name="Definition">http://example.com/</text:span></text:a></text:p> </text:list-item> <text:list-item> <text:p text:style-name="P55">It should.</text:p> </text:list-item> </text:list> <text:p text:style-name="First_20_paragraph">An e-mail address: -<text:a xlink:type="simple" xlink:href="mailto:nobody@nowhere.net" office:name=""><text:span text:style-name="Definition"><text:span text:style-name="Teletype">nobody@nowhere.net</text:span></text:span></text:a></text:p> +<text:a xlink:type="simple" xlink:href="mailto:nobody@nowhere.net" office:name=""><text:span text:style-name="Definition">nobody@nowhere.net</text:span></text:a></text:p> <text:p text:style-name="P56">Blockquoted: -<text:a xlink:type="simple" xlink:href="http://example.com/" office:name=""><text:span text:style-name="Definition"><text:span text:style-name="Teletype">http://example.com/</text:span></text:span></text:a></text:p> +<text:a xlink:type="simple" xlink:href="http://example.com/" office:name=""><text:span text:style-name="Definition">http://example.com/</text:span></text:a></text:p> <text:p text:style-name="First_20_paragraph">Auto-links should not occur here: <text:span text:style-name="Teletype"><http://example.com/></text:span></text:p> <text:p text:style-name="P57">or here: <http://example.com/></text:p> @@ -1589,10 +1581,10 @@ indented to show that they belong to the footnote (as with list items).</text:p><text:p text:style-name="P58"><text:s text:c="2" />{ <code> }</text:p><text:p text:style-name="Footnote">If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.</text:p></text:note-body></text:note> This -should <text:span text:style-name="T75">not</text:span> be a footnote +should <text:span text:style-name="T67">not</text:span> be a footnote reference, because it contains a space.[^my note] Here is an inline note.<text:note text:id="ftn2" text:note-class="footnote"><text:note-citation>3</text:note-citation><text:note-body><text:p text:style-name="Footnote">This -is <text:span text:style-name="T76">easier</text:span> to type. Inline notes +is <text:span text:style-name="T68">easier</text:span> to type. Inline notes may contain <text:a xlink:type="simple" xlink:href="http://google.com" office:name=""><text:span text:style-name="Definition">links</text:span></text:a> and <text:span text:style-name="Teletype">]</text:span> verbatim characters, diff --git a/tests/writer.org b/tests/writer.org index 642b2a3ef..b8058a406 100644 --- a/tests/writer.org +++ b/tests/writer.org @@ -711,7 +711,7 @@ With an ampersand: [[http://example.com/?foo=1&bar=2]] - [[http://example.com/]] - It should. -An e-mail address: [[mailto:nobody@nowhere.net][=nobody@nowhere.net=]] +An e-mail address: [[mailto:nobody@nowhere.net][nobody@nowhere.net]] #+BEGIN_QUOTE Blockquoted: [[http://example.com/]] diff --git a/tests/writer.rst b/tests/writer.rst index 8d7c7915c..41da5bc73 100644 --- a/tests/writer.rst +++ b/tests/writer.rst @@ -18,8 +18,8 @@ markdown test suite. Headers ======= -Level 2 with an `embedded link </url>`_ ---------------------------------------- +Level 2 with an `embedded link </url>`__ +---------------------------------------- Level 3 with *emphasis* ~~~~~~~~~~~~~~~~~~~~~~~ @@ -59,8 +59,8 @@ item. Here’s one with a bullet. \* criminey. -There should be a hard line break -here. +| There should be a hard line break +| here. -------------- @@ -549,7 +549,7 @@ This is *emphasized*, and so *is this*. This is **strong**, and so **is this**. -An *`emphasized link </url>`_*. +An *`emphasized link </url>`__*. ***This is strong and em.*** @@ -584,7 +584,7 @@ Smart quotes, ellipses, dashes ‘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>`_”. +link <http://example.com/?foo=1&bar=2>`__”. Some dashes: one—two — three—four — five. @@ -690,42 +690,42 @@ Links Explicit -------- -Just a `URL </url/>`_. +Just a `URL </url/>`__. -`URL and title </url/>`_. +`URL and title </url/>`__. -`URL and title </url/>`_. +`URL and title </url/>`__. -`URL and title </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>`_ +`with\_underscore </url/with_underscore>`__ -`Email link <mailto:nobody@nowhere.net>`_ +`Email link <mailto:nobody@nowhere.net>`__ -`Empty <>`_. +`Empty <>`__. Reference --------- -Foo `bar </url/>`_. +Foo `bar </url/>`__. -Foo `bar </url/>`_. +Foo `bar </url/>`__. -Foo `bar </url/>`_. +Foo `bar </url/>`__. -With `embedded [brackets] </url/>`_. +With `embedded [brackets] </url/>`__. -`b </url/>`_ by itself should be a link. +`b </url/>`__ by itself should be a link. -Indented `once </url>`_. +Indented `once </url>`__. -Indented `twice </url>`_. +Indented `twice </url>`__. -Indented `thrice </url>`_. +Indented `thrice </url>`__. This should [not][] be a link. @@ -733,21 +733,21 @@ This should [not][] be a link. [not]: /url -Foo `bar </url/>`_. +Foo `bar </url/>`__. -Foo `biz </url/>`_. +Foo `biz </url/>`__. With ampersands --------------- Here’s a `link with an ampersand in the -URL <http://example.com/?foo=1&bar=2>`_. +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 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 </script?foo=1&bar=2>`__. -Here’s an `inline link in pointy braces </script?foo=1&bar=2>`_. +Here’s an `inline link in pointy braces </script?foo=1&bar=2>`__. Autolinks --------- @@ -776,7 +776,6 @@ Images From “Voyage dans la Lune” by Georges Melies (1902): .. figure:: lalune.jpg - :align: center :alt: Voyage dans la Lune lalune @@ -816,7 +815,7 @@ This paragraph should not be part of the note, as it is not indented. .. [3] This is *easier* to type. Inline notes may contain - `links <http://google.com>`_ and ``]`` verbatim characters, as well as + `links <http://google.com>`__ and ``]`` verbatim characters, as well as [bracketed text]. .. [4] diff --git a/tests/writer.rtf b/tests/writer.rtf index 648b4966d..42c13d8c7 100644 --- a/tests/writer.rtf +++ b/tests/writer.rtf @@ -407,21 +407,21 @@ inline link in pointy braces .\par} {\pard \ql \f0 \sa180 \li0 \fi0 \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 -{\f1 http://example.com/?foo=1&bar=2} +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 -{\f1 http://example.com/} +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 -{\f1 nobody@nowhere.net} +nobody@nowhere.net }}} \par} {\pard \ql \f0 \sa180 \li720 \fi0 Blockquoted: {\field{\*\fldinst{HYPERLINK "http://example.com/"}}{\fldrslt{\ul -{\f1 http://example.com/} +http://example.com/ }}} \par} {\pard \ql \f0 \sa180 \li0 \fi0 Auto-links should not occur here: {\f1 <http://example.com/>}\par} diff --git a/tests/writer.texinfo b/tests/writer.texinfo index aed237f2a..7b59ea651 100644 --- a/tests/writer.texinfo +++ b/tests/writer.texinfo @@ -30,7 +30,7 @@ _@{\text\@} @title Pandoc Test Suite @author John MacFarlane @author Anonymous -July 17@comma{} 2006 +July 17, 2006 @end titlepage @node Top @@ -64,28 +64,33 @@ This is a set of tests for pandoc. Most of them are adapted from John Gruber's m @node Headers @chapter Headers +@anchor{#headers} @menu * Level 2 with an embedded link:: @end menu @node Level 2 with an embedded link @section Level 2 with an @uref{/url,embedded link} +@anchor{#level-2-with-an-embedded-link} @menu * Level 3 with emphasis:: @end menu @node Level 3 with emphasis @subsection Level 3 with @emph{emphasis} +@anchor{#level-3-with-emphasis} @menu * Level 4:: @end menu @node Level 4 @subsubsection Level 4 +@anchor{#level-4} Level 5 @node Level 1 @chapter Level 1 +@anchor{#level-1} @menu * Level 2 with emphasis:: * Level 2:: @@ -93,16 +98,19 @@ Level 5 @node Level 2 with emphasis @section Level 2 with @emph{emphasis} +@anchor{#level-2-with-emphasis} @menu * Level 3:: @end menu @node Level 3 @subsection Level 3 +@anchor{#level-3} with no blank line @node Level 2 @section Level 2 +@anchor{#level-2} with no blank line @iftex @@ -114,6 +122,7 @@ with no blank line @node Paragraphs @chapter Paragraphs +@anchor{#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. @@ -131,6 +140,7 @@ There should be a hard line break@*here. @node Block Quotes @chapter Block Quotes +@anchor{#block-quotes} E-mail style: @quotation @@ -138,6 +148,7 @@ This is a block quote. It is pretty short. @end quotation @quotation Code in a block quote: + @verbatim sub status { print "working"; @@ -175,7 +186,9 @@ And a following paragraph. @node Code Blocks @chapter Code Blocks +@anchor{#code-blocks} Code: + @verbatim ---- (should be four hyphens) @@ -187,6 +200,7 @@ this code block is indented by one tab @end verbatim And: + @verbatim this code block is indented by two tabs @@ -202,6 +216,7 @@ These should not be escaped: \$ \\ \> \[ \{ @node Lists @chapter Lists +@anchor{#lists} @menu * Unordered:: * Ordered:: @@ -212,6 +227,7 @@ These should not be escaped: \$ \\ \> \[ \{ @node Unordered @section Unordered +@anchor{#unordered} Asterisks tight: @itemize @@ -289,6 +305,7 @@ Minus 3 @node Ordered @section Ordered +@anchor{#ordered} Tight: @enumerate @@ -343,7 +360,7 @@ Multiple paragraphs: @enumerate @item -Item 1@comma{} graf one. +Item 1, graf one. Item 1. graf two. The quick brown fox jumped over the lazy dog's back. @@ -357,6 +374,7 @@ Item 3. @node Nested @section Nested +@anchor{#nested} @itemize @item Tab @@ -417,6 +435,7 @@ Third @node Tabs and spaces @section Tabs and spaces +@anchor{#tabs-and-spaces} @itemize @item this is a list item indented with tabs @@ -437,6 +456,7 @@ this is an example list item indented with spaces @node Fancy list markers @section Fancy list markers +@anchor{#fancy-list-markers} @enumerate 2 @item begins with 2 @@ -447,7 +467,7 @@ with a continuation @enumerate 4 @item -sublist with roman numerals@comma{} starting with 4 +sublist with roman numerals, starting with 4 @item more items @enumerate A @@ -512,6 +532,7 @@ B. Williams @node Definition Lists @chapter Definition Lists +@anchor{#definition-lists} Tight using spaces: @table @asis @@ -564,11 +585,12 @@ Multiple blocks with italics: red fruit -contains seeds@comma{} crisp@comma{} pleasant to taste +contains seeds, crisp, pleasant to taste @item @emph{orange} orange fruit + @verbatim { orange code block } @end verbatim @@ -578,7 +600,7 @@ orange block quote @end quotation @end table -Multiple definitions@comma{} tight: +Multiple definitions, tight: @table @asis @item apple @@ -591,7 +613,7 @@ orange fruit bank @end table -Multiple definitions@comma{} loose: +Multiple definitions, loose: @table @asis @item apple @@ -608,7 +630,7 @@ bank @end table -Blank line after term@comma{} indented marker@comma{} alternate markers: +Blank line after term, indented marker, alternate markers: @table @asis @item apple @@ -632,6 +654,7 @@ sublist @node HTML Blocks @chapter HTML Blocks +@anchor{#html-blocks} Simple block on one line: foo @@ -646,7 +669,8 @@ And this is @strong{strong} Here's a simple block: foo -This should be a code block@comma{} though: +This should be a code block, though: + @verbatim <div> foo @@ -654,11 +678,12 @@ This should be a code block@comma{} though: @end verbatim As should this: + @verbatim <div>foo</div> @end verbatim -Now@comma{} nested: +Now, nested: foo This should just be an HTML comment: @@ -666,13 +691,15 @@ This should just be an HTML comment: Multiline: Code block: + @verbatim <!-- Comment --> @end verbatim -Just plain comment@comma{} with trailing spaces on the line: +Just plain comment, with trailing spaces on the line: Code: + @verbatim <hr /> @end verbatim @@ -688,9 +715,10 @@ Hr's: @node Inline Markup @chapter Inline Markup -This is @emph{emphasized}@comma{} and so @emph{is this}. +@anchor{#inline-markup} +This is @emph{emphasized}, and so @emph{is this}. -This is @strong{strong}@comma{} and so @strong{is this}. +This is @strong{strong}, and so @strong{is this}. An @emph{@uref{/url,emphasized link}}. @@ -702,15 +730,15 @@ So is @strong{@emph{this}} word. So is @strong{@emph{this}} word. -This is code: @code{>}@comma{} @code{$}@comma{} @code{\}@comma{} @code{\$}@comma{} @code{<html>}. +This is code: @code{>}, @code{$}, @code{\}, @code{\$}, @code{<html>}. @textstrikeout{This is @emph{strikeout}.} Superscripts: a@textsuperscript{bc}d a@textsuperscript{@emph{hello}} a@textsuperscript{hello@ there}. -Subscripts: H@textsubscript{2}O@comma{} H@textsubscript{23}O@comma{} H@textsubscript{many@ of@ them}O. +Subscripts: H@textsubscript{2}O, H@textsubscript{23}O, H@textsubscript{many@ of@ them}O. -These should not be superscripts or subscripts@comma{} because of the unescaped spaces: a^b c^d@comma{} a~b c~d. +These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d. @iftex @bigskip@hrule@bigskip @@ -720,20 +748,21 @@ These should not be superscripts or subscripts@comma{} because of the unescaped @end ifnottex @node Smart quotes ellipses dashes -@chapter Smart quotes@comma{} ellipses@comma{} dashes -``Hello@comma{}'' said the spider. ```Shelob' is my name.'' +@chapter Smart quotes, ellipses, dashes +@anchor{#smart-quotes-ellipses-dashes} +``Hello,'' said the spider. ```Shelob' is my name.'' -`A'@comma{} `B'@comma{} and `C' are letters. +`A', `B', and `C' are letters. -`Oak@comma{}' `elm@comma{}' and `beech' are names of trees. So is `pine.' +`Oak,' `elm,' and `beech' are names of trees. So is `pine.' -`He said@comma{} ``I want to go.''' Were you alive in the 70's? +`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@comma{} 255--66@comma{} 1987--1999. +Dashes between numbers: 5--7, 255--66, 1987--1999. Ellipses@dots{}and@dots{}and@dots{}. @@ -746,6 +775,7 @@ Ellipses@dots{}and@dots{}and@dots{}. @node LaTeX @chapter LaTeX +@anchor{#latex} @itemize @item @tex @@ -771,9 +801,9 @@ These shouldn't be math: @itemize @item -To get the famous equation@comma{} write @code{$e = mc^2$}. +To get the famous equation, write @code{$e = mc^2$}. @item -$22@comma{}000 is a @emph{lot} of money. So is $34@comma{}000. (It worked if ``lot'' is emphasized.) +$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 @@ -798,6 +828,7 @@ Cat & 1 \\ \hline @node Special Characters @chapter Special Characters +@anchor{#special-characters} Here is some unicode: @itemize @@ -864,6 +895,7 @@ Minus: - @node Links @chapter Links +@anchor{#links} @menu * Explicit:: * Reference:: @@ -873,6 +905,7 @@ Minus: - @node Explicit @section Explicit +@anchor{#explicit} Just a @uref{/url/,URL}. @uref{/url/,URL and title}. @@ -893,6 +926,7 @@ Just a @uref{/url/,URL}. @node Reference @section Reference +@anchor{#reference} Foo @uref{/url/,bar}. Foo @uref{/url/,bar}. @@ -910,6 +944,7 @@ Indented @uref{/url,twice}. Indented @uref{/url,thrice}. This should [not][] be a link. + @verbatim [not]: /url @end verbatim @@ -920,6 +955,7 @@ Foo @uref{/url/,biz}. @node With ampersands @section With ampersands +@anchor{#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}. @@ -930,6 +966,7 @@ Here's an @uref{/script?foo=1&bar=2,inline link in pointy braces}. @node Autolinks @section Autolinks +@anchor{#autolinks} With an ampersand: @url{http://example.com/?foo=1&bar=2} @itemize @@ -941,12 +978,13 @@ In a list? It should. @end itemize -An e-mail address: @uref{mailto:nobody@@nowhere.net,@code{nobody@@nowhere.net}} +An e-mail address: @uref{mailto:nobody@@nowhere.net,nobody@@nowhere.net} @quotation Blockquoted: @url{http://example.com/} @end quotation Auto-links should not occur here: @code{<http://example.com/>} + @verbatim or here: <http://example.com/> @end verbatim @@ -960,6 +998,7 @@ or here: <http://example.com/> @node Images @chapter Images +@anchor{#images} From ``Voyage dans la Lune'' by Georges Melies (1902): @float @@ -978,14 +1017,16 @@ Here is a movie @image{movie,,,movie,jpg} icon. @node Footnotes @chapter Footnotes -Here is a footnote reference@comma{}@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. +@anchor{#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 { <code> } @end verbatim -If you want@comma{} you can indent every line@comma{} but you can also be lazy and just indent the first line of each block.} This should @emph{not} be a footnote reference@comma{} 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@comma{} as well as [bracketed text].} +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.} @@ -995,6 +1036,6 @@ Notes can go in quotes.@footnote{In quote.} And in list items.@footnote{In list.} @end enumerate -This paragraph should not be part of the note@comma{} as it is not indented. +This paragraph should not be part of the note, as it is not indented. @bye diff --git a/tests/writer.textile b/tests/writer.textile index 51aca5a08..20767b13c 100644 --- a/tests/writer.textile +++ b/tests/writer.textile @@ -2,31 +2,31 @@ This is a set of tests for pandoc. Most of them are adapted from John Gruber's m <hr /> -h1. Headers +h1(#headers). Headers -h2. Level 2 with an "embedded link":/url +h2(#level-2-with-an-embedded-link). Level 2 with an "embedded link":/url -h3. Level 3 with _emphasis_ +h3(#level-3-with-emphasis). Level 3 with _emphasis_ -h4. Level 4 +h4(#level-4). Level 4 -h5. Level 5 +h5(#level-5). Level 5 -h1. Level 1 +h1(#level-1). Level 1 -h2. Level 2 with _emphasis_ +h2(#level-2-with-emphasis). Level 2 with _emphasis_ -h3. Level 3 +h3(#level-3). Level 3 with no blank line -h2. Level 2 +h2(#level-2). Level 2 with no blank line <hr /> -h1. Paragraphs +h1(#paragraphs). Paragraphs Here's a regular paragraph. @@ -39,7 +39,7 @@ here. <hr /> -h1. Block Quotes +h1(#block-quotes). Block Quotes E-mail style: @@ -79,7 +79,7 @@ And a following paragraph. <hr /> -h1. Code Blocks +h1(#code-blocks). Code Blocks Code: @@ -103,9 +103,9 @@ These should not be escaped: \$ \\ \> \[ \{ <hr /> -h1. Lists +h1(#lists). Lists -h2. Unordered +h2(#unordered). Unordered Asterisks tight: @@ -143,7 +143,7 @@ Minuses loose: * Minus 2 * Minus 3 -h2. Ordered +h2(#ordered). Ordered Tight: @@ -178,7 +178,7 @@ Multiple paragraphs: <li><p>Item 3.</p></li> </ol> -h2. Nested +h2(#nested). Nested * Tab ** Tab @@ -202,14 +202,14 @@ Same thing but with paragraphs: #* Foe # Third -h2. Tabs and spaces +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 +h2(#fancy-list-markers). Fancy list markers <ol start="2" style="list-style-type: decimal;"> <li>begins with 2</li> @@ -259,7 +259,7 @@ B. Williams <hr /> -h1. Definition Lists +h1(#definition-lists). Definition Lists Tight using spaces: @@ -347,7 +347,7 @@ Blank line after term, indented marker, alternate markers: </dd> </dl> -h1. HTML Blocks +h1(#html-blocks). HTML Blocks Simple block on one line: @@ -464,7 +464,7 @@ Hr's: <hr /> -h1. Inline Markup +h1(#inline-markup). Inline Markup This is _emphasized_, and so _is this_. @@ -492,7 +492,7 @@ These should not be superscripts or subscripts, because of the unescaped spaces: <hr /> -h1. Smart quotes, ellipses, dashes +h1(#smart-quotes-ellipses-dashes). Smart quotes, ellipses, dashes "Hello," said the spider. "'Shelob' is my name." @@ -512,7 +512,7 @@ Ellipses...and...and.... <hr /> -h1. LaTeX +h1(#latex). LaTeX * * <span class="math">2+2=4</math> @@ -535,7 +535,7 @@ Here's a LaTeX table: <hr /> -h1. Special Characters +h1(#special-characters). Special Characters Here is some unicode: @@ -589,9 +589,9 @@ Minus: - <hr /> -h1. Links +h1(#links). Links -h2. Explicit +h2(#explicit). Explicit Just a "URL":/url/. @@ -611,7 +611,7 @@ Just a "URL":/url/. "Empty":. -h2. Reference +h2(#reference). Reference Foo "bar":/url/. @@ -638,7 +638,7 @@ Foo "bar":/url/. Foo "biz":/url/. -h2. With ampersands +h2(#with-ampersands). With ampersands Here's a "link with an ampersand in the URL":http://example.com/?foo=1&bar=2. @@ -648,17 +648,17 @@ 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 +h2(#autolinks). Autolinks -With an ampersand: "http://example.com/?foo=1&bar=2":http://example.com/?foo=1&bar=2 +With an ampersand: "$":http://example.com/?foo=1&bar=2 * In a list? -* "http://example.com/":http://example.com/ +* "$":http://example.com/ * It should. -An e-mail address: "nobody@nowhere.net":mailto:nobody@nowhere.net +An e-mail address: "nobody@nowhere.net":mailto:nobody@nowhere.net -bq. Blockquoted: "http://example.com/":http://example.com/ +bq. Blockquoted: "$":http://example.com/ @@ -669,7 +669,7 @@ bc. or here: <http://example.com/> <hr /> -h1. Images +h1(#images). Images From "Voyage dans la Lune" by Georges Melies (1902): @@ -680,7 +680,7 @@ Here is a movie !movie.jpg(movie)! icon. <hr /> -h1. Footnotes +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] |