diff options
220 files changed, 6514 insertions, 4141 deletions
diff --git a/.travis.yml b/.travis.yml index 26557f6f8..1b9b9a095 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - env: BUILD=cabal GHCVER=7.8.4 CABALVER=1.18 compiler: ": #GHC 7.8.4" addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4,happy-1.19.5], sources: [hvr-ghc]}} + - env: BUILD=cabal GHCVER=7.10.3 CABALVER=1.22 compiler: ": #GHC 7.10.3" addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3,happy-1.19.5], sources: [hvr-ghc]}} @@ -100,13 +101,11 @@ install: case "$BUILD" in stack) ulimit -n 4096 - stack --no-terminal install hsb2hs stack --no-terminal --install-ghc $ARGS test --flag 'aeson:fast' --only-dependencies --fast --flag pandoc:embed_data_files --test ;; cabal) cabal --version travis_retry cabal update - cabal install hsb2hs cabal install -j --only-dependencies -ffast --enable-tests --enable-benchmarks -fembed_data_files --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS ;; esac @@ -32,33 +32,53 @@ statements for these sources are included below. All are GPL-compatible licenses. ---------------------------------------------------------------------- +src/Text/Pandoc/Writers/Muse.hs +Copyright (C) 2017 Alexander Krotov + +Released under the GNU General Public License version 2 or later. + +---------------------------------------------------------------------- src/Text/Pandoc/Writers/Texinfo.hs -Copyright (C) 2008-2015 John MacFarlane and Peter Wang +Copyright (C) 2008-2017 John MacFarlane and Peter Wang Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/OpenDocument.hs -Copyright (C) 2008-2015 Andrea Rossato and John MacFarlane +Copyright (C) 2008-2017 Andrea Rossato and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Writers/Org.hs -Copyright (C) 2010-2015 Puneeth Chaganti and John MacFarlane +Copyright (C) 2010-2017 Puneeth Chaganti, John MacFarlane, and + Albert Krewinkel + +Released under the GNU General Public License version 2 or later. + +---------------------------------------------------------------------- +src/Text/Pandoc/Writers/ZimWiki.hs +Copyright (C) 2017 Alex Ivkin + +Released under the GNU General Public License version 2 or later. + +---------------------------------------------------------------------- +src/Text/Pandoc/Readers/Docx.hs +src/Text/Pandoc/Readers/Docx/* +Copyright (C) 2014-2017 Jesse Rosenthal Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Textile.hs -Copyright (C) 2010-2015 Paul Rivier and John MacFarlane +Copyright (C) 2010-2017 Paul Rivier and John MacFarlane Released under the GNU General Public License version 2 or later. ---------------------------------------------------------------------- src/Text/Pandoc/Readers/Org.hs test/Tests/Readers/Org.hs -Copyright (C) 2014-2015 Albert Krewinkel +Copyright (C) 2014-2017 Albert Krewinkel Released under the GNU General Public License version 2 or later. @@ -77,6 +97,12 @@ Copyright (C) 2004 Peter Jipsen http://www.chapman.edu/~jipsen Released under the GNU General Public License version 2 or later. ------------------------------------------------------------------------ +data/pandoc.lua +Copyright (C) 2017 Albert Krewinkel + +Released under the GNU General Public License version 2 or later. + +---------------------------------------------------------------------- The dzslides template contains javascript and CSS from Paul Rouget's dzslides template. http://github.com/paulrouget/dzslides diff --git a/MANUAL.txt b/MANUAL.txt index 9b5c27b77..df08e4b40 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -523,10 +523,15 @@ Reader options `--extract-media=`*DIR* -: Extract images and other media contained in a docx or epub container - to the path *DIR*, creating it if necessary, and adjust the images - references in the document so they point to the extracted files. - This option only affects the docx and epub readers. +: Extract images and other media contained in or linked from + the source document to the path *DIR*, creating it if + necessary, and adjust the images references in the document + so they point to the extracted files. If the source format is + a binary container (docx, epub, or odt), the media is + extracted from the container and the original + filenames are used. Otherwise the media is read from the + file system or downloaded, and new filenames are constructed + based on SHA1 hashes of the contents. `--abbreviations=`*FILE* @@ -588,7 +593,15 @@ General writer options : Print a system default data file. Files in the user data directory are ignored. +`--eol=crlf`|`lf`|`native` + +: Manually specify line endings: `crlf` (Windows), `lf` + (MacOS/linux/unix), or `native` (line endings appropriate + to the OS on which pandoc is being run). The default is + `native`. + `--dpi`=*NUMBER* + : Specify the dpi (dots per inch) value for conversion from pixels to inch/centimeters and vice versa. The default is 96dpi. Technically, the correct term would be ppi (pixels per inch). @@ -678,6 +691,18 @@ General writer options repeatedly to include multiple files. They will be included in the order specified. Implies `--standalone`. +`--resource-path=`*SEARCHPATH* + +: List of paths to search for images and other resources. + The paths should be separated by `:` on linux, unix, and + MacOS systems, and by `;` on Windows. If `--resource-path` + is not specified, the default resource path is the working + directory. Note that, if `--resource-path` is specified, + the working directory must be explicitly listed or it + will not be searched. For example: + `--resource-path=.:test` will search the working directory + and the `test` subdirectory, in that order. + Options affecting specific writers ---------------------------------- @@ -828,47 +853,51 @@ Options affecting specific writers : Use the specified file as a style reference in producing a docx or ODT file. - Docx: For best results, the reference docx should be a modified - version of a docx file produced using pandoc. The contents - of the reference docx are ignored, but its stylesheets and - document properties (including margins, page size, header, - and footer) are used in the new docx. If no reference docx - is specified on the command line, pandoc will look for a - file `reference.docx` in the user data directory (see - `--data-dir`). If this is not found either, sensible - defaults will be used. - - To produce a custom `reference.docx`, first get a copy of - the default `reference.docx`: `pandoc - --print-default-data-file reference.docx > - custom-reference.docx`. Then open `custom-reference.docx` - in Word, modify the styles as you wish, and save the file. - For best results, do not make changes to this file other - than modifying the styles used by pandoc: [paragraph] - Normal, Body Text, First Paragraph, Compact, Title, - Subtitle, Author, Date, Abstract, Bibliography, Heading 1, - Heading 2, Heading 3, Heading 4, Heading 5, Heading 6, - Heading 7, Heading 8, Heading 9, Block Text, Footnote Text, - Definition Term, Definition, Caption, Table Caption, - Image Caption, Figure, Figure With Caption, TOC Heading; - [character] Default Paragraph Font, Body Text Char, - Verbatim Char, Footnote Reference, Hyperlink; [table] - Normal Table. - - ODT: For best results, the reference ODT should be a modified - version of an ODT produced using pandoc. The contents of - the reference ODT are ignored, but its stylesheets are used - in the new ODT. If no reference ODT is specified on the - command line, pandoc will look for a file `reference.odt` in - the user data directory (see `--data-dir`). If this is not - found either, sensible defaults will be used. - - To produce a custom `reference.odt`, first get a copy of - the default `reference.odt`: `pandoc - --print-default-data-file reference.odt > - custom-reference.odt`. Then open `custom-reference.odt` in - LibreOffice, modify the styles as you wish, and save the - file. + Docx + + : For best results, the reference docx should be a modified + version of a docx file produced using pandoc. The contents + of the reference docx are ignored, but its stylesheets and + document properties (including margins, page size, header, + and footer) are used in the new docx. If no reference docx + is specified on the command line, pandoc will look for a + file `reference.docx` in the user data directory (see + `--data-dir`). If this is not found either, sensible + defaults will be used. + + To produce a custom `reference.docx`, first get a copy of + the default `reference.docx`: `pandoc + --print-default-data-file reference.docx > + custom-reference.docx`. Then open `custom-reference.docx` + in Word, modify the styles as you wish, and save the file. + For best results, do not make changes to this file other + than modifying the styles used by pandoc: [paragraph] + Normal, Body Text, First Paragraph, Compact, Title, + Subtitle, Author, Date, Abstract, Bibliography, Heading 1, + Heading 2, Heading 3, Heading 4, Heading 5, Heading 6, + Heading 7, Heading 8, Heading 9, Block Text, Footnote Text, + Definition Term, Definition, Caption, Table Caption, + Image Caption, Figure, Captioned Figure, TOC Heading; + [character] Default Paragraph Font, Body Text Char, + Verbatim Char, Footnote Reference, Hyperlink; [table] + Table. + + ODT + + : For best results, the reference ODT should be a modified + version of an ODT produced using pandoc. The contents of + the reference ODT are ignored, but its stylesheets are used + in the new ODT. If no reference ODT is specified on the + command line, pandoc will look for a file `reference.odt` in + the user data directory (see `--data-dir`). If this is not + found either, sensible defaults will be used. + + To produce a custom `reference.odt`, first get a copy of + the default `reference.odt`: `pandoc + --print-default-data-file reference.odt > + custom-reference.odt`. Then open `custom-reference.odt` in + LibreOffice, modify the styles as you wish, and save the + file. `--epub-cover-image=`*FILE* @@ -1310,6 +1339,11 @@ including all [reveal.js configuration options]. : color for internal links, citation links, external links, and links in table of contents: uses any of the [predefined LaTeX colors] (for beamer only). +`aspectratio` +: aspect ratio of slides (for beamer only, `1610` for 16:10, `169` for 16:9, + `149` for 14:9, `141` for 1.41:1, `54` for 5:4, `43` for 4:3 which is the + default, and `32` for 3:2). + [reveal.js configuration options]: https://github.com/hakimel/reveal.js#configuration Variables for LaTeX @@ -3071,7 +3105,8 @@ definition, which may occur elsewhere in the document (either 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 +square brackets. (There cannot be space between the two unless the +`spaced_reference_links` extension is enabled.) 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. The label must not be parseable as a citation (assuming @@ -3522,6 +3557,13 @@ implied by pandoc's default `all_symbols_escapable`. Allow a list to occur right after a paragraph, with no intervening blank space. +#### Extension: `spaced_reference_links` #### + +Allow whitespace between the two components of a reference link, +for example, + + [foo] [bar]. + #### Extension: `hard_line_breaks` #### Causes all newlines within a paragraph to be interpreted as hard line @@ -4059,7 +4101,7 @@ Syntax highlighting =================== Pandoc will automatically highlight syntax in [fenced code blocks] that -are marked with a language name. The Haskell library [highlighting-kate] is +are marked with a language name. The Haskell library [skylighting] is used for highlighting, which works in HTML, Docx, Ms, and LaTeX/PDF output. To see a list of language names that pandoc will recognize, type `pandoc --list-highlight-languages`. @@ -4072,7 +4114,7 @@ type `pandoc --list-highlight-styles`. To disable highlighting, use the `--no-highlight` option. -[highlighting-kate]: https://github.com/jgm/highlighting-kate +[skylighting]: https://github.com/jgm/skylighting Custom Styles in Docx Output ============================ @@ -4139,7 +4181,7 @@ which you can modify according to your needs, do Authors ======= -© 2006-2016 John MacFarlane (jgm@berkeley.edu). Released under the +© 2006-2017 John MacFarlane (jgm@berkeley.edu). Released under the [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) @@ -18,9 +18,15 @@ test: bench: stack bench +weigh: + stack build --flag 'pandoc:weigh-pandoc' && stack exec weigh-pandoc + reformat: for f in $(sourcefiles); do echo $$f; stylish-haskell -i $$f ; done +lint: + for f in $(sourcefiles); do echo $$f; hlint --verbose --refactor --refactor-options='-i -s' $$f; done + changes_github: pandoc --filter extract-changes.hs changelog -t markdown_github | sed -e 's/\\#/#/g' | pbcopy @@ -40,8 +46,8 @@ macospkg: man/pandoc.1 winpkg: pandoc-$(version)-windows.msi pandoc-$(version)-windows.msi: - wget 'https://ci.appveyor.com/api/projects/jgm/pandoc/artifacts/windows/pandoc.msi?branch=$(BRANCH)' -O pandoc.msi && \ - osslsigncode sign -pkcs12 ~/Private/ComodoCodeSigning.exp2017.p12 -in pandoc.msi -i http://johnmacfarlane.net/ -t http://timestamp.comodoca.com/ -out $@ -askpass + wget 'https://ci.appveyor.com/api/projects/jgm/pandoc/artifacts/windows/pandoc-windows-i386.msi?branch=$(BRANCH)' -O pandoc.msi && \ + osslsigncode sign -pkcs12 ~/Private/ComodoCodeSigning.exp2019.p12 -in pandoc.msi -i http://johnmacfarlane.net/ -t http://timestamp.comodoca.com/ -out $@ -askpass rm pandoc.msi man/pandoc.1: MANUAL.txt man/pandoc.1.template @@ -59,4 +65,4 @@ download_stats: clean: stack clean -.PHONY: deps quick full install clean test bench changes_github macospkg dist prof download_stats reformat +.PHONY: deps quick full install clean test bench changes_github macospkg dist prof download_stats reformat lint weigh @@ -140,7 +140,7 @@ new issue. License ------- -© 2006-2016 John MacFarlane (jgm@berkeley.edu). Released under the +© 2006-2017 John MacFarlane (jgm@berkeley.edu). Released under the [GPL], version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) diff --git a/appveyor.yml b/appveyor.yml index 373e1bfe4..2977b4656 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,31 @@ -clone_folder: "c:\\stack" +clone_folder: "c:\\pandoc" environment: global: - STACK_ROOT: "c:\\sr" - STACK_YAML: "c:\\stack\\stack.pkg.yaml" - WIXBIN: "c:\\Program Files (x86)\\WiX Toolset v3.10\\bin" + WIXBIN: "c:\\Program Files (x86)\\WiX Toolset v3.11\\bin" + STACK_YAML: "c:\\pandoc\\stack.pkg.yaml" + matrix: + - STACK_VERSION: "windows-i386" + STACK_ROOT: "c:\\sr32" + STACK: "%STACK_ROOT%\\stack.exe" +# - STACK_VERSION: "windows-x86_64" +# STACK_ROOT: "c:\\sr64" +# STACK: "%STACK_ROOT%\\stack.exe" + +matrix: + fast_finish: true cache: - - "c:\\sr" # stack root, short paths == fewer problems - - "c:\\stack\\stack.exe" - - '%WIXBIN%' + - "%STACK_ROOT%" + - "%WIXBIN%" + # This is where stack install ghc by default, but we don't + # cache it because it's too large: + # - "c:\\Users\\appveyor\\AppData\\Local\\Programs\\stack" + +# Note: to reset build cache, do the following in JavaScript +# console on appveyor: +# $.ajax({ +# url: 'https://ci.appveyor.com/api/projects/jgm/pandoc/buildcache', +# type: 'DELETE'}) # We don't do a normal C build, but build in test_script via stack build: off @@ -16,39 +33,35 @@ build: off install: - '"%WIXBIN%"\candle -? || choco install wixtoolset' - | - stack --version || curl -ostack.zip -L --insecure http://www.stackage.org/stack/windows-i386 && 7z x stack.zip stack.exe - - stack setup > nul + %STACK% --version || curl -ostack.zip -L --insecure http://www.stackage.org/stack/%STACK_VERSION% && 7z e stack.zip -o"%STACK_ROOT%" stack.exe -before_test: - # the stack install already fails without the templates... - - git submodule update --init - # set PATH to where the hsb2hs binary is copied to - - cmd: set "PATH=%PATH%;%APPDATA%\\local\\bin" - - stack install hsb2hs +# before_test: test_script: # The ugly echo "" hack is to avoid complaints about 0 being an invalid file # descriptor - - echo "" | stack clean - - echo "" | stack -j1 --no-terminal test - - echo "" | stack -j1 --local-bin-path=.\windows install pandoc pandoc-citeproc + - | + %STACK% setup > nul + %STACK% path + echo "" | %STACK% clean + echo "" | %STACK% -j1 --no-terminal --test --local-bin-path=.\windows install pandoc pandoc-citeproc after_test: # .\ in the stack commandline seems to be .\windows\ (where the stack-appveyor.yaml is) - cd windows - - 7z a "pandoc.zip" pandoc.exe - .\pandoc.exe -s --toc ..\MANUAL.txt -o MANUAL.html - .\pandoc.exe -s ..\COPYING.md -o COPYING.rtf - copy ..\COPYRIGHT COPYRIGHT.txt + - 7z a "pandoc-%STACK_VERSION%.zip" pandoc.exe pandoc-citeproc.exe MANUAL.html COPYING.rtf - | set VERSION= for /f "tokens=1-2 delims= " %%a in ('.\pandoc.exe --version') do ( if not defined VERSION set "VERSION=%%b" ) echo %VERSION% "%WIXBIN%\\candle" -dVERSION=%VERSION% -dBINPATH=. *.wxs -out wixobj\ - "%WIXBIN%\\light" -sw1076 -ext WixUIExtension -ext WixUtilExtension -cultures:en-us -loc Pandoc-en-us.wxl -out pandoc.msi wixobj\*.wixobj + "%WIXBIN%\\light" -sw1076 -ext WixUIExtension -ext WixUtilExtension -cultures:en-us -loc Pandoc-en-us.wxl -out "pandoc-%STACK_VERSION%.msi" wixobj\*.wixobj artifacts: - - path: windows\pandoc.zip + - path: 'windows\pandoc-%STACK_VERSION%.zip' name: exe - - path: windows\pandoc.msi + - path: 'windows\pandoc-%STACK_VERSION%.msi' name: msi diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index 7294c74a4..2f0242ac4 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} import Text.Pandoc import Text.Pandoc.Class hiding (getCurrentTime) +import qualified Text.Pandoc.UTF8 as UTF8 +import Data.Text (Text) import Data.Time (getCurrentTime) import qualified Data.ByteString as B import qualified Data.Map as Map @@ -27,11 +29,11 @@ import Debug.Trace (trace) import System.Environment (getArgs) readerBench :: Pandoc - -> (String, ReaderOptions -> String -> Pandoc) + -> (String, ReaderOptions -> Text -> Pandoc) -> Maybe Benchmark readerBench doc (name, reader) = case lookup name writers of - Just (StringWriter writer) -> + Just (TextWriter writer) -> let inp = either (error . show) id $ runPure $ writer def{ writerWrapText = WrapAuto} doc in return $ bench (name ++ " reader") $ nf @@ -39,7 +41,7 @@ readerBench doc (name, reader) = _ -> trace ("\nCould not find writer for " ++ name ++ "\n") Nothing writerBench :: Pandoc - -> (String, WriterOptions -> Pandoc -> String) + -> (String, WriterOptions -> Pandoc -> Text) -> Benchmark writerBench doc (name, writer) = bench (name ++ " writer") $ nf (writer def{ writerWrapText = WrapAuto }) doc @@ -47,13 +49,13 @@ writerBench doc (name, writer) = bench (name ++ " writer") $ nf main :: IO () main = do args <- getArgs - let matchReader (n, StringReader _) = + let matchReader (n, TextReader _) = case args of [] -> True [x] -> x == n (x:y:_) -> x == n && y == "reader" matchReader (_, _) = False - let matchWriter (n, StringWriter _) = + let matchWriter (n, TextWriter _) = case args of [] -> True [x] -> x == n @@ -61,7 +63,7 @@ main = do matchWriter (_, _) = False let matchedReaders = filter matchReader readers let matchedWriters = filter matchWriter writers - inp <- readFile "test/testsuite.txt" + inp <- UTF8.toText <$> B.readFile "test/testsuite.txt" lalune <- B.readFile "test/lalune.jpg" movie <- B.readFile "test/movie.jpg" time <- getCurrentTime @@ -74,12 +76,12 @@ main = do let doc = either (error . show) id $ runPure $ readMarkdown opts inp let readers' = [(n, \o d -> either (error . show) id $ runPure $ r o d) - | (n, StringReader r) <- matchedReaders] + | (n, TextReader r) <- matchedReaders] let readerBs = mapMaybe (readerBench doc) $ filter (\(n,_) -> n /="haddock") readers' let writers' = [(n, \o d -> either (error . show) id $ runPure $ setupFakeFiles >> w o d) - | (n, StringWriter w) <- matchedWriters] + | (n, TextWriter w) <- matchedWriters] let writerBs = map (writerBench doc) $ writers' defaultMainWith defaultConfig{ timeLimit = 6.0 } diff --git a/benchmark/weigh-pandoc.hs b/benchmark/weigh-pandoc.hs index b4462d747..d3cada8c0 100644 --- a/benchmark/weigh-pandoc.hs +++ b/benchmark/weigh-pandoc.hs @@ -1,5 +1,6 @@ import Weigh import Text.Pandoc +import Data.Text (Text) main :: IO () main = do @@ -23,13 +24,13 @@ main = do ,("commonmark", writeCommonMark) ] -weighWriter :: Pandoc -> String -> (Pandoc -> String) -> Weigh () +weighWriter :: Pandoc -> String -> (Pandoc -> Text) -> Weigh () weighWriter doc name writer = func (name ++ " writer") writer doc -weighReader :: Pandoc -> String -> (String -> Pandoc) -> Weigh () +weighReader :: Pandoc -> String -> (Text -> Pandoc) -> Weigh () weighReader doc name reader = do case lookup name writers of - Just (StringWriter writer) -> + Just (TextWriter writer) -> let inp = either (error . show) id $ runPure $ writer def{ writerWrapText = WrapAuto} doc in func (name ++ " reader") reader inp _ -> return () -- no writer for reader diff --git a/data/docx/word/styles.xml b/data/docx/word/styles.xml index 5d09a67b0..3596d8bbc 100644 --- a/data/docx/word/styles.xml +++ b/data/docx/word/styles.xml @@ -342,8 +342,9 @@ <w:semiHidden /> <w:unhideWhenUsed /> </w:style> - <w:style w:type="table" w:default="1" w:styleId="TableNormal"> - <w:name w:val="Normal Table" /> + <w:style w:type="table" w:default="1" w:styleId="Table"> + <w:name w:val="Table" /> + <w:basedOn w:val="TableNormal" /> <w:semiHidden /> <w:unhideWhenUsed /> <w:qFormat /> @@ -400,8 +401,8 @@ <w:name w:val="Figure" /> <w:basedOn w:val="Normal" /> </w:style> - <w:style w:type="paragraph" w:customStyle="1" w:styleId="FigureWithCaption"> - <w:name w:val="Figure with Caption" /> + <w:style w:type="paragraph" w:customStyle="1" w:styleId="CaptionedFigure"> + <w:name w:val="Captioned Figure" /> <w:basedOn w:val="Figure" /> <w:pPr> <w:keepNext /> diff --git a/data/pandoc.lua b/data/pandoc.lua index 2a5a945b5..750b41e86 100644 --- a/data/pandoc.lua +++ b/data/pandoc.lua @@ -233,11 +233,11 @@ M.BulletList = M.Block:create_constructor( --- Creates a code block element -- @function CodeBlock -- @tparam string text code string --- @tparam[opt] Attr attributes element attributes +-- @tparam[opt] Attr attr element attributes -- @treturn Block code block element M.CodeBlock = M.Block:create_constructor( "CodeBlock", - function(text, attributes) return {c = {attributes, text}} end, + function(text, attr) return {c = {attr or M.Attr(), text}} end, {{"identifier", "classes", "attributes"}, "text"} ) @@ -254,11 +254,13 @@ M.DefinitionList = M.Block:create_constructor( --- Creates a div element -- @function Div -- @tparam {Block,...} content block content --- @tparam[opt] Attr attributes element attributes +-- @tparam[opt] Attr attr element attributes -- @treturn Block code block element M.Div = M.Block:create_constructor( "Div", - function(content, attributes) return {c = {attributes, content}} end, + function(content, attr) + return {c = {attr or M.Attr(), content}} + end, {{"identifier", "classes", "attributes"}, "content"} ) @@ -266,12 +268,12 @@ M.Div = M.Block:create_constructor( -- @function Header -- @tparam int level header level -- @tparam {Inline,...} content inline content --- @tparam Attr attributes element attributes +-- @tparam[opt] Attr attr element attributes -- @treturn Block header element M.Header = M.Block:create_constructor( "Header", - function(level, content, attributes) - return {c = {level, attributes, content}} + function(level, content, attr) + return {c = {level, attr or M.Attr(), content}} end, {"level", {"identifier", "classes", "attributes"}, "content"} ) @@ -386,11 +388,11 @@ M.Cite = M.Inline:create_constructor( --- Creates a Code inline element -- @function Code -- @tparam string text brief image description --- @tparam[opt] Attr attributes additional attributes +-- @tparam[opt] Attr attr additional attributes -- @treturn Inline code element M.Code = M.Inline:create_constructor( "Code", - function(text, attributes) return {c = {attributes, text}} end, + function(text, attr) return {c = {attr or M.Attr(), text}} end, {{"identifier", "classes", "attributes"}, "text"} ) @@ -409,14 +411,14 @@ M.Emph = M.Inline:create_constructor( -- @tparam {Inline,..} caption text used to describe the image -- @tparam string src path to the image file -- @tparam[opt] string title brief image description --- @tparam[opt] Attr attributes additional attributes +-- @tparam[opt] Attr attr additional attributes -- @treturn Inline image element M.Image = M.Inline:create_constructor( "Image", - function(caption, src, title, attributes) + function(caption, src, title, attr) title = title or "" - attributes = attributes or Attribute.empty - return {c = {attributes, caption, {src, title}}} + attr = attr or M.Attr() + return {c = {attr, caption, {src, title}}} end, {"attributes", "caption", {"src", "title"}} ) @@ -434,14 +436,14 @@ M.LineBreak = M.Inline:create_constructor( -- @tparam {Inline,..} content text for this link -- @tparam string target the link target -- @tparam[opt] string title brief link description --- @tparam[opt] Attr attributes additional attributes +-- @tparam[opt] Attr attr additional attributes -- @treturn Inline image element M.Link = M.Inline:create_constructor( "Link", - function(content, target, title, attributes) + function(content, target, title, attr) title = title or "" - attributes = attributes or Attribute.empty - return {c = {attributes, content, {target, title}}} + attr = attr or M.Attr() + return {c = {attr, content, {target, title}}} end, {"attributes", "content", {"target", "title"}} ) @@ -560,11 +562,11 @@ M.Space = M.Inline:create_constructor( --- Creates a Span inline element -- @function Span -- @tparam {Inline,..} content inline content --- @tparam[opt] Attr attributes additional attributes +-- @tparam[opt] Attr attr additional attributes -- @treturn Inline span element M.Span = M.Inline:create_constructor( "Span", - function(content, attributes) return {c = {attributes, content}} end, + function(content, attr) return {c = {attr or M.Attr(), content}} end, {{"identifier", "classes", "attributes"}, "content"} ) diff --git a/data/templates/default.beamer b/data/templates/default.beamer index fb02cb058..c9d30b657 100644 --- a/data/templates/default.beamer +++ b/data/templates/default.beamer @@ -1,4 +1,4 @@ -\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(handout)$handout,$endif$$if(colorlinks)$dvipsnames,$endif$$if(beamer)$ignorenonframetext,$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$} +\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(handout)$handout,$endif$$if(colorlinks)$dvipsnames,$endif$$if(beamer)$ignorenonframetext,$endif$$for(classoption)$$classoption$$sep$,$endfor$$if(aspectratio)$aspectratio=$aspectratio$,$endif$]{$documentclass$} \setbeamertemplate{caption}[numbered] \setbeamertemplate{caption label separator}{: } \setbeamercolor{caption name}{fg=normal text.fg} diff --git a/data/templates/default.context b/data/templates/default.context index 4a3457934..e17d85b36 100644 --- a/data/templates/default.context +++ b/data/templates/default.context @@ -23,6 +23,7 @@ $endif$ style=$linkstyle$, color=$linkcolor$, contrastcolor=$linkcontrastcolor$] + % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option=bookmark] @@ -37,26 +38,22 @@ $endif$ $if(pagenumbering)$ \setuppagenumbering[$for(pagenumbering)$$pagenumbering$$sep$,$endfor$] $endif$ + % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] + \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted -\usemodule[simplefonts$if(fontsize)$,$fontsize$$endif$] -\setmainfontfallback[DejaVu Serif][range={greekandcoptic, greekextended}, force=yes, rscale=auto] -$if(mainfont)$ -\setmainfont[$mainfont$] -$endif$ -$if(sansfont)$ -\setsansfont[$sansfont$][rscale=auto] -$endif$ -$if(monofont)$ -\setmonofont[$monofont$][features=none, rscale=auto] -$endif$ -$if(mathfont)$ -\setmathfont[$mathfont$][rscale=auto] -$endif$ + +\definefallbackfamily[mainface][rm][DejaVu Serif][preset=range:greek, force=yes] +\definefontfamily[mainface][rm][$if(mainfont)$$mainfont$$else$Latin Modern Roman$endif$] +\definefontfamily[mainface][mm][$if(mathfont)$$mathfont$$else$Latin Modern Math$endif$] +\definefontfamily[mainface][ss][$if(sansfont)$$sansfont$$else$Latin Modern Sans$endif$] +\definefontfamily[mainface][tt][$if(monofont)$$monofont$$else$Latin Modern Typewriter$endif$][features=none] +\setupbodyfont[mainface$if(fontsize)$,$fontsize$$endif$] + \setupwhitespace[$if(whitespace)$$whitespace$$else$medium$endif$] $if(indenting)$ \setupindenting[$for(indenting)$$indenting$$sep$,$endfor$] diff --git a/data/templates/default.latex b/data/templates/default.latex index 899a00aea..156c43985 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -69,6 +69,15 @@ $endif$ \usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} +$if(indent)$ +$else$ +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} +$endif$ \PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref $if(verbatim-in-note)$ \usepackage{fancyvrb} @@ -103,20 +112,6 @@ $endif$ $if(geometry)$ \usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry} $endif$ -$if(lang)$ -\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex - \usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel} -$if(babel-newcommands)$ - $babel-newcommands$ -$endif$ -\else - \usepackage{polyglossia} - \setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$} -$for(polyglossia-otherlangs)$ - \setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$} -$endfor$ -\fi -$endif$ $if(natbib)$ \usepackage{natbib} \bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$} @@ -155,22 +150,13 @@ $if(graphics)$ $endif$ $if(links-as-notes)$ % Make links footnotes instead of hotlinks: -\renewcommand{\href}[2]{#2\footnote{\url{#1}}} +\DeclareRobustCommand{\href}[2]{#2\footnote{\url{#1}}} $endif$ $if(strikeout)$ \usepackage[normalem]{ulem} % avoid problems with \sout in headers with hyperref: \pdfstringdefDisableCommands{\renewcommand{\sout}{}} $endif$ -$if(indent)$ -$else$ -\IfFileExists{parskip.sty}{% -\usepackage{parskip} -}{% else -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -} -$endif$ \setlength{\emergencystretch}{3em} % prevent overfull lines \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} @@ -191,6 +177,21 @@ $else$ \renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}} \fi $endif$ +$if(lang)$ +\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex + \usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel} +$if(babel-newcommands)$ + $babel-newcommands$ +$endif$ +\else + % load polyglossia as late as possible as it *could* call bidi if RTL lang (e.g. Hebrew or Arabic) + \usepackage{polyglossia} + \setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$} +$for(polyglossia-otherlangs)$ + \setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$} +$endfor$ +\fi +$endif$ $if(dir)$ \ifxetex % load bidi as late as possible as it modifies e.g. graphicx diff --git a/linux/control.in b/linux/control.in index d1aa865ce..7653fa823 100644 --- a/linux/control.in +++ b/linux/control.in @@ -5,16 +5,20 @@ Priority: optional Architecture: ARCHITECTURE Installed-Size: INSTALLED_SIZE Depends: libc6 (>= 2.13), libgmp10, zlib1g (>= 1:1.1.4) +Suggests: texlive-latex-recommended, texlive-xetex, + texlive-fonts-recommended Maintainer: John MacFarlane <jgm@berkeley.edu> Description: general markup converter Pandoc is a Haskell library for converting from one markup format to another, and a command-line tool that uses - this library. It can read markdown and (subsets of) HTML, - reStructuredText, LaTeX, DocBook, MediaWiki markup, Twiki markup, - Haddock markup, OPML, Emacs Org-Mode, txt2tags and Textile, and - it can write markdown, reStructuredText, HTML, LaTeX, ConTeXt, - Docbook, OPML, OpenDocument, ODT, Word docx, RTF, MediaWiki, - DokuWiki, Textile, groff man pages, plain text, Emacs Org-Mode, - AsciiDoc, Haddock markup, EPUB (v2 and v3), FictionBook2, - InDesign ICML, Muse, and several kinds of HTML/javascript - slide shows (S5, Slidy, Slideous, DZSlides, reveal.js). + this library. It can read several dialects of Markdown and + (subsets of) HTML, reStructuredText, LaTeX, DocBook, MediaWiki + markup, Word docx, ODT, EPUB, TWiki markup, Haddock markup, OPML, + Emacs Org-Mode, txt2tags and Textile, and it can write Markdown, + reStructuredText, HTML, LaTeX, ConTeXt, DocBook, OPML, GNU TexInfo, + OpenDocument, ODT, Word docx, RTF, MediaWiki, ZimWiki, + DokuWiki, Textile, groff man pages, groff ms, plain text, + Emacs Org-Mode, AsciiDoc, Haddock markup, EPUB (v2 and v3), + FictionBook2, InDesign ICML, TEI Simple, JATS XML, Muse, + and several kinds of HTML/javascript slide shows (S5, Slidy, + Slideous, DZSlides, reveal.js). diff --git a/macos/make_macos_package.sh b/macos/make_macos_package.sh index c5d073cdc..f49b6a81b 100755 --- a/macos/make_macos_package.sh +++ b/macos/make_macos_package.sh @@ -7,6 +7,7 @@ VERSION=$(grep -e '^Version' pandoc.cabal | awk '{print $2}') RESOURCES=$DIST/Resources ROOT=$DIST/pandoc DEST=$ROOT/usr/local +PANDOC=$DEST/bin/pandoc SCRIPTS=$MACOS/macos-resources BASE=pandoc-$VERSION ME=$(whoami) @@ -20,12 +21,14 @@ export MACMACOS_DEPLOYMENT_TARGET=10.7 rm -rf $DIST mkdir -p $DIST mkdir -p $RESOURCES +mkdir -p $DEST/bin +mkdir -p $DEST/share/man/man1 + stack setup -which cpphs || stack install cpphs echo Building pandoc... stack clean -stack install --stack-yaml=stack.pkg.yaml --local-bin-path . pandoc pandoc-citeproc +stack install --stack-yaml=stack.pkg.yaml --local-bin-path $DEST/bin/ pandoc pandoc-citeproc echo Getting man pages... make man/pandoc.1 @@ -36,18 +39,13 @@ PANDOC_CITEPROC_TARBALL=https://hackage.haskell.org/package/pandoc-citeproc-${PA curl ${PANDOC_CITEPROC_TARBALL} | tar xzC $DIST PANDOC_CITEPROC_PATH=$DIST/pandoc-citeproc-${PANDOC_CITEPROC_VERSION} -mkdir -p $DEST/bin -mkdir -p $DEST/share/man/man1 -for f in pandoc pandoc-citeproc; do - cp $MACOS/$f $DEST/bin/; -done cp $PANDOC_CITEPROC_PATH/man/man1/pandoc-citeproc.1 $DEST/share/man/man1/ cp man/pandoc.1 $DEST/share/man/man1/ chown -R $ME:staff $DIST echo Copying license... -$MACOS/pandoc --data data -t html5 -s COPYING.md -o $RESOURCES/license.html +$PANDOC --data data -t html5 -s COPYING.md -Vpagetitle="License" -o $RESOURCES/license.html # Removing executable signing because of a problem that arose in El Capitan # "source=obsolete resource envelope" @@ -62,11 +60,11 @@ echo Creating MacOS package... sed -e "s/PANDOCVERSION/$VERSION/" $MACOS/distribution.xml.in > $MACOS/distribution.xml -pkgbuild --root $DIST/pandoc --identifier net.johnmacfarlane.pandoc --version 1.13 --ownership recommended $DIST/pandoc.pkg +pkgbuild --root $ROOT --identifier net.johnmacfarlane.pandoc --version $VERSION --ownership recommended $DIST/pandoc.pkg productbuild --distribution $MACOS/distribution.xml --resources $DIST/Resources --package-path $DIST --version $VERSION --sign "${DEVELOPER_ID_INSTALLER}" $BASE-MacOS.pkg # verify signature spctl --assess --type install $BASE-MacOS.pkg # cleanup -rm -r $DIST $MACOS/pandoc $MACOS/pandoc-citeproc +rm -r $DIST diff --git a/man/pandoc.1 b/man/pandoc.1 index a9cb65854..15dbcbe07 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,5 +1,5 @@ .\"t -.TH PANDOC 1 "January 29, 2017" "pandoc 1.19.2" +.TH PANDOC 1 "March 26, 2017" "pandoc 2.0" .SH NAME pandoc - general markup converter .SH SYNOPSIS @@ -18,10 +18,11 @@ Markdown, MultiMarkdown, reStructuredText, XHTML, HTML5, LaTeX (including \f[C]beamer\f[] slide shows), ConTeXt, RTF, OPML, DocBook, OpenDocument, ODT, Word docx, GNU Texinfo, MediaWiki markup, DokuWiki markup, ZimWiki markup, Haddock markup, EPUB (v2 or v3), FictionBook2, -Textile, groff man pages, Emacs Org mode, AsciiDoc, InDesign ICML, TEI -Simple, and Slidy, Slideous, DZSlides, reveal.js or S5 HTML slide shows. -It can also produce PDF output on systems where LaTeX, ConTeXt, or -\f[C]wkhtmltopdf\f[] is installed. +Textile, groff man, groff ms, Emacs Org mode, AsciiDoc, InDesign ICML, +TEI Simple, and Slidy, Slideous, DZSlides, reveal.js or S5 HTML slide +shows. +It can also produce PDF output on systems where LaTeX, ConTeXt, +\f[C]pdfroff\f[], or \f[C]wkhtmltopdf\f[] is installed. .PP Pandoc's enhanced version of Markdown includes syntax for footnotes, tables, flexible ordered lists, definition lists, fenced code blocks, @@ -57,7 +58,7 @@ 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[], -\f[C]epub\f[], and \f[C]epub3\f[] output formats). +\f[C]epub2\f[], and \f[C]epub3\f[] output formats). For output to a file, use the \f[C]\-o\f[] option: .IP .nf @@ -89,6 +90,15 @@ pandoc\ \-f\ html\ \-t\ markdown\ http://www.fsf.org \f[] .fi .PP +It is possible to supply a custom User\-Agent string when requesting a +document from a URL, by setting an environment variable: +.IP +.nf +\f[C] +USER_AGENT="Mozilla/5.0"\ pandoc\ \-f\ html\ \-t\ markdown\ http://www.fsf.org +\f[] +.fi +.PP If multiple input files are given, \f[C]pandoc\f[] will concatenate them all (with blank lines between them) before parsing. This feature is disabled for binary input formats such as \f[C]EPUB\f[], @@ -172,29 +182,32 @@ pandoc\ test.txt\ \-o\ test.pdf Production of a PDF requires that a LaTeX engine be installed (see \f[C]\-\-latex\-engine\f[], below), and assumes that the following LaTeX packages are available: \f[C]amsfonts\f[], \f[C]amsmath\f[], -\f[C]lm\f[], \f[C]ifxetex\f[], \f[C]ifluatex\f[], \f[C]eurosym\f[], -\f[C]listings\f[] (if the \f[C]\-\-listings\f[] option is used), -\f[C]fancyvrb\f[], \f[C]longtable\f[], \f[C]booktabs\f[], -\f[C]graphicx\f[] and \f[C]grffile\f[] (if the document contains -images), \f[C]hyperref\f[], \f[C]ulem\f[], \f[C]geometry\f[] (with the -\f[C]geometry\f[] variable set), \f[C]setspace\f[] (with -\f[C]linestretch\f[]), and \f[C]babel\f[] (with \f[C]lang\f[]). +\f[C]lm\f[], \f[C]unicode\-math\f[], \f[C]ifxetex\f[], +\f[C]ifluatex\f[], \f[C]eurosym\f[], \f[C]listings\f[] (if the +\f[C]\-\-listings\f[] option is used), \f[C]fancyvrb\f[], +\f[C]longtable\f[], \f[C]booktabs\f[], \f[C]graphicx\f[] and +\f[C]grffile\f[] (if the document contains images), \f[C]hyperref\f[], +\f[C]ulem\f[], \f[C]geometry\f[] (with the \f[C]geometry\f[] variable +set), \f[C]setspace\f[] (with \f[C]linestretch\f[]), and \f[C]babel\f[] +(with \f[C]lang\f[]). The use of \f[C]xelatex\f[] or \f[C]lualatex\f[] as the LaTeX engine -requires \f[C]fontspec\f[]; \f[C]xelatex\f[] uses \f[C]mathspec\f[], -\f[C]polyglossia\f[] (with \f[C]lang\f[]), \f[C]xecjk\f[], and -\f[C]bidi\f[] (with the \f[C]dir\f[] variable set). +requires \f[C]fontspec\f[]. +\f[C]xelatex\f[] uses \f[C]polyglossia\f[] (with \f[C]lang\f[]), +\f[C]xecjk\f[], and \f[C]bidi\f[] (with the \f[C]dir\f[] variable set). +If the \f[C]mathspec\f[] variable is set, \f[C]xelatex\f[] will use +\f[C]mathspec\f[] instead of \f[C]unicode\-math\f[]. The \f[C]upquote\f[] and \f[C]microtype\f[] packages are used if -available, and \f[C]csquotes\f[] will be used for smart punctuation if +available, and \f[C]csquotes\f[] will be used for [smart punctuation] if added to the template or included in any header file. The \f[C]natbib\f[], \f[C]biblatex\f[], \f[C]bibtex\f[], and \f[C]biber\f[] packages can optionally be used for citation rendering. These are included with all recent versions of TeX Live. .PP -Alternatively, pandoc can use ConTeXt or \f[C]wkhtmltopdf\f[] to create -a PDF. +Alternatively, pandoc can use ConTeXt, \f[C]wkhtmltopdf\f[], or +\f[C]pdfroff\f[] to create a PDF. To do this, specify an output file with a \f[C]\&.pdf\f[] extension, as -before, but add \f[C]\-t\ context\f[] or \f[C]\-t\ html5\f[] to the -command line. +before, but add \f[C]\-t\ context\f[], \f[C]\-t\ html5\f[], or +\f[C]\-t\ ms\f[] to the command line. .PP PDF output can be controlled using variables for LaTeX (if LaTeX is used) and variables for ConTeXt (if ConTeXt is used). @@ -243,18 +256,20 @@ Specify output format. (original unextended Markdown), \f[C]markdown_phpextra\f[] (PHP Markdown Extra), \f[C]markdown_github\f[] (GitHub\-Flavored Markdown), \f[C]markdown_mmd\f[] (MultiMarkdown), \f[C]commonmark\f[] (CommonMark -Markdown), \f[C]rst\f[] (reStructuredText), \f[C]html\f[] (XHTML), -\f[C]html5\f[] (HTML5), \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]dokuwiki\f[] (DokuWiki +Markdown), \f[C]rst\f[] (reStructuredText), \f[C]html4\f[] (XHTML 1.0 +Transitional), \f[C]html\f[] or \f[C]html5\f[] (HTML5/XHTML polyglot +markup), \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]dokuwiki\f[] (DokuWiki markup), \f[C]zimwiki\f[] (ZimWiki markup), \f[C]textile\f[] (Textile), \f[C]org\f[] (Emacs Org mode), \f[C]texinfo\f[] (GNU Texinfo), -\f[C]opml\f[] (OPML), \f[C]docbook\f[] (DocBook 4), \f[C]docbook5\f[] -(DocBook 5), \f[C]opendocument\f[] (OpenDocument), \f[C]odt\f[] -(OpenOffice text document), \f[C]docx\f[] (Word docx), \f[C]haddock\f[] -(Haddock markup), \f[C]rtf\f[] (rich text format), \f[C]epub\f[] (EPUB -v2 book), \f[C]epub3\f[] (EPUB v3), \f[C]fb2\f[] (FictionBook2 e\-book), -\f[C]asciidoc\f[] (AsciiDoc), \f[C]icml\f[] (InDesign ICML), +\f[C]opml\f[] (OPML), \f[C]docbook\f[] or \f[C]docbook4\f[] (DocBook 4), +\f[C]docbook5\f[] (DocBook 5), \f[C]jats\f[] (JATS XML), +\f[C]opendocument\f[] (OpenDocument), \f[C]odt\f[] (OpenOffice text +document), \f[C]docx\f[] (Word docx), \f[C]haddock\f[] (Haddock markup), +\f[C]rtf\f[] (rich text format), \f[C]epub2\f[] (EPUB v2 book), +\f[C]epub\f[] or \f[C]epub3\f[] (EPUB v3), \f[C]fb2\f[] (FictionBook2 +e\-book), \f[C]asciidoc\f[] (AsciiDoc), \f[C]icml\f[] (InDesign ICML), \f[C]tei\f[] (TEI Simple), \f[C]slidy\f[] (Slidy HTML and JavaScript slide show), \f[C]slideous\f[] (Slideous HTML and JavaScript slide show), \f[C]dzslides\f[] (DZSlides HTML5 + JavaScript slide show), @@ -265,7 +280,7 @@ 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 +\f[C]latex\f[], \f[C]beamer\f[], \f[C]html4\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 @@ -340,6 +355,23 @@ Currently this only has an effect with PDF output. .RS .RE .TP +.B \f[C]\-\-quiet\f[] +Suppress warning messages. +.RS +.RE +.TP +.B \f[C]\-\-fail\-if\-warnings\f[] +Exit with error status if there are any warnings. +.RS +.RE +.TP +.B \f[C]\-\-log=\f[]\f[I]FILE\f[] +Write log messages in machine\-readable JSON format to \f[I]FILE\f[]. +All messages above DEBUG level will be written, regardless of verbosity +settings (\f[C]\-\-verbose\f[], \f[C]\-\-quiet\f[]). +.RS +.RE +.TP .B \f[C]\-\-list\-input\-formats\f[] List supported input formats, one per line. .RS @@ -379,41 +411,6 @@ Show usage message. .RE .SS Reader options .TP -.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. -Raw HTML can be printed in Markdown, reStructuredText, Emacs Org mode, -HTML, Slidy, Slideous, DZSlides, reveal.js, and S5 output; raw LaTeX can -be printed in Markdown, reStructuredText, Emacs Org mode, 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.) -.RS -.RE -.TP -.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. -Nonbreaking spaces are inserted after certain abbreviations, such as -\[lq]Mr.\[rq] (Note: This option is selected automatically when the -output format is \f[C]latex\f[] or \f[C]context\f[], unless -\f[C]\-\-no\-tex\-ligatures\f[] is used. -It has no effect for \f[C]latex\f[] input.) -.RS -.RE -.TP -.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. -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[] Specify the base level for headers (defaults to 1). .RS @@ -487,6 +484,37 @@ a specified full or relative path (executable or non\-executable) \f[C]$PATH\f[] (executable only) .RE .TP +.B \f[C]\-\-lua\-filter=\f[]\f[I]SCRIPT\f[] +Transform the document in a similar fashion as JSON filters (see +\f[C]\-\-filter\f[]), but use pandoc's build\-in lua filtering system. +The given lua script is expected to return a list of lua filters which +will be applied in order. +Each lua filter must contain element\-transforming functions indexed by +the name of the AST element on which the filter function should be +applied. +.RS +.PP +The \f[C]pandoc\f[] lua module provides helper functions for element +creation. +It is always loaded into the script's lua environment. +.PP +The following is an example lua script for macro\-expansion: +.IP +.nf +\f[C] +function\ expand_hello_world(inline) +\ \ if\ inline.c\ ==\ \[aq]{{helloworld}}\[aq]\ then +\ \ \ \ return\ pandoc.Emph{\ pandoc.Str\ "Hello,\ World"\ } +\ \ else +\ \ \ \ return\ inline +\ \ end +end + +return\ {{Str\ =\ expand_hello_world}} +\f[] +.fi +.RE +.TP .B \f[C]\-M\f[] \f[I]KEY\f[][\f[C]=\f[]\f[I]VAL\f[]], \f[C]\-\-metadata=\f[]\f[I]KEY\f[][\f[C]:\f[]\f[I]VAL\f[]] Set the metadata field \f[I]KEY\f[] to the value \f[I]VAL\f[]. A value specified on the command line overrides a value specified in the @@ -501,13 +529,6 @@ and may be printed in some output formats). .RS .RE .TP -.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[] Preserve tabs instead of converting them to spaces (the default). Note that this will only affect tabs in literal code spans and code @@ -539,10 +560,29 @@ This option only affects the docx reader. .RE .TP .B \f[C]\-\-extract\-media=\f[]\f[I]DIR\f[] -Extract images and other media contained in a docx or epub container to -the path \f[I]DIR\f[], creating it if necessary, and adjust the images -references in the document so they point to the extracted files. -This option only affects the docx and epub readers. +Extract images and other media contained in or linked from the source +document to the path \f[I]DIR\f[], creating it if necessary, and adjust +the images references in the document so they point to the extracted +files. +If the source format is a binary container (docx, epub, or odt), the +media is extracted from the container and the original filenames are +used. +Otherwise the media is read from the file system or downloaded, and new +filenames are constructed based on SHA1 hashes of the contents. +.RS +.RE +.TP +.B \f[C]\-\-abbreviations=\f[]\f[I]FILE\f[] +Specifies a custom abbreviations file, with abbreviations one to a line. +If this option is not specified, pandoc will read the data file +\f[C]abbreviations\f[] from the user data directory or fall back on a +system default. +To see the system default, use +\f[C]pandoc\ \-\-print\-default\-data\-file=abbreviations\f[]. +The only use pandoc makes of this list is in the Markdown reader. +Strings ending in a period that are found in this list will be followed +by a nonbreaking space, so that the period will not produce +sentence\-ending space in formats like LaTeX. .RS .RE .SS General writer options @@ -595,6 +635,14 @@ Files in the user data directory are ignored. .RS .RE .TP +.B \f[C]\-\-eol=crlf\f[]|\f[C]lf\f[]|\f[C]native\f[] +Manually specify line endings: \f[C]crlf\f[] (Windows), \f[C]lf\f[] +(MacOS/linux/unix), or \f[C]native\f[] (line endings appropriate to the +OS on which pandoc is being run). +The default is \f[C]native\f[]. +.RS +.RE +.TP .B \f[C]\-\-dpi\f[]=\f[I]NUMBER\f[] Specify the dpi (dots per inch) value for conversion from pixels to inch/centimeters and vice versa. @@ -617,11 +665,6 @@ Automatic wrapping does not currently work in HTML output. .RS .RE .TP -.B \f[C]\-\-no\-wrap\f[] -Deprecated synonym for \f[C]\-\-wrap=none\f[]. -.RS -.RE -.TP .B \f[C]\-\-columns=\f[]\f[I]NUMBER\f[] Specify length of lines in characters. This affects text wrapping in the generated source code (see @@ -633,11 +676,11 @@ Tables below). .TP .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[], \f[C]docx\f[], and \f[C]rst\f[], an -instruction to create one) in the output document. -This option has no effect on \f[C]man\f[], \f[C]docbook\f[], -\f[C]docbook5\f[], \f[C]slidy\f[], \f[C]slideous\f[], \f[C]s5\f[], or -\f[C]odt\f[] output. +\f[C]latex\f[], \f[C]context\f[], \f[C]docx\f[], \f[C]rst\f[], or +\f[C]ms\f[], an instruction to create one) in the output document. +This option has no effect on \f[C]man\f[], \f[C]docbook4\f[], +\f[C]docbook5\f[], \f[C]jats\f[], \f[C]slidy\f[], \f[C]slideous\f[], +\f[C]s5\f[], or \f[C]odt\f[] output. .RS .RE .TP @@ -655,7 +698,7 @@ 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[]|\f[I]FILE\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]breezeDark\f[], \f[C]espresso\f[], @@ -664,6 +707,21 @@ For more information on syntax highlighting in pandoc, see Syntax highlighting, below. See also \f[C]\-\-list\-highlight\-styles\f[]. .RS +.PP +Instead of a \f[I]STYLE\f[] name, a JSON file with extension +\f[C]\&.theme\f[] may be supplied. +This will be parsed as a KDE syntax highlighting theme and (if valid) +used as the highlighting style. +To see a sample theme that can be modified, +\f[C]pandoc\ \-\-print\-default\-data\-file\ default.theme\f[]. +.RE +.TP +.B \f[C]\-\-syntax\-definition=\f[]\f[I]FILE\f[] +Instructs pandoc to load a KDE XML syntax definition file, which will be +used for syntax highlighting of appropriately marked code blocks. +This can be used to add support for new languages or to use altered +syntax definitions for existing languages. +.RS .RE .TP .B \f[C]\-H\f[] \f[I]FILE\f[], \f[C]\-\-include\-in\-header=\f[]\f[I]FILE\f[] @@ -698,6 +756,19 @@ They will be included in the order specified. Implies \f[C]\-\-standalone\f[]. .RS .RE +.TP +.B \f[C]\-\-resource\-path=\f[]\f[I]SEARCHPATH\f[] +List of paths to search for images and other resources. +The paths should be separated by \f[C]:\f[] on linux, unix, and MacOS +systems, and by \f[C];\f[] on Windows. +If \f[C]\-\-resource\-path\f[] is not specified, the default resource +path is the working directory. +Note that, if \f[C]\-\-resource\-path\f[] is specified, the working +directory must be explicitly listed or it will not be searched. +For example: \f[C]\-\-resource\-path=.:test\f[] will search the working +directory and the \f[C]test\f[] subdirectory, in that order. +.RS +.RE .SS Options affecting specific writers .TP .B \f[C]\-\-self\-contained\f[] @@ -708,13 +779,16 @@ The resulting file should be \[lq]self\-contained,\[rq] in the sense that it needs no external files and no net access to be displayed properly by a browser. This option works only with HTML output formats, including -\f[C]html\f[], \f[C]html5\f[], \f[C]html+lhs\f[], \f[C]html5+lhs\f[], +\f[C]html4\f[], \f[C]html5\f[], \f[C]html+lhs\f[], \f[C]html5+lhs\f[], \f[C]s5\f[], \f[C]slidy\f[], \f[C]slideous\f[], \f[C]dzslides\f[], and \f[C]revealjs\f[]. Scripts, images, and stylesheets at absolute URLs will be downloaded; those at relative URLs will be sought relative to the working directory (if the first source file is local) or relative to the base URL (if the first source file is remote). +Elements with the attribute \f[C]data\-external="1"\f[] will be left +alone; the documents they link to will not be incorporated in the +document. Limitation: resources that are loaded dynamically through JavaScript cannot be incorporated; as a result, \f[C]\-\-self\-contained\f[] does not work with \f[C]\-\-mathjax\f[], and some advanced features @@ -730,8 +804,8 @@ Use \f[C]<q>\f[] tags for quotes in HTML. .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). +Currently supported only for HTML and DocBook output (which uses +numerical entities instead of UTF\-8 when this option is selected). .RS .RE .TP @@ -760,11 +834,6 @@ ATX headers. .RS .RE .TP -.B \f[C]\-\-chapters\f[] -Deprecated synonym for \f[C]\-\-top\-level\-division=chapter\f[]. -.RS -.RE -.TP .B \f[C]\-\-top\-level\-division=[default|section|chapter|part]\f[] Treat top\-level headers as the given division type in LaTeX, ConTeXt, DocBook, and TEI output. @@ -805,25 +874,6 @@ Implies \f[C]\-\-number\-sections\f[]. .RS .RE .TP -.B \f[C]\-\-no\-tex\-ligatures\f[] -Do not use the TeX ligatures for quotation marks, apostrophes, and -dashes (\f[C]`...\[aq]\f[], \f[C]``..\[aq]\[aq]\f[], \f[C]\-\-\f[], -\f[C]\-\-\-\f[]) when writing or reading LaTeX or ConTeXt. -In reading LaTeX, parse the characters \f[C]`\f[], \f[C]\[aq]\f[], and -\f[C]\-\f[] literally, rather than parsing ligatures for quotation marks -and dashes. -In writing LaTeX or ConTeXt, print unicode quotation mark and dash -characters literally, rather than converting them to the standard ASCII -TeX ligatures. -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[]. -.RS -.RE -.TP .B \f[C]\-\-listings\f[] Use the \f[C]listings\f[] package for LaTeX code blocks .RS @@ -842,6 +892,8 @@ Specifies that headers with the specified level create slides (for Headers above this level in the hierarchy are used to divide the slide show into sections; headers below this level create subheads within a slide. +Note that content that is not contained under slide\-level headers will +not appear in the slide show. The default is to set the slide level based on the contents of the document; see Structuring the slide show. .RS @@ -867,8 +919,9 @@ The default is \f[C]none\f[]. .RE .TP .B \f[C]\-\-id\-prefix=\f[]\f[I]STRING\f[] -Specify a prefix to be added to all automatically generated identifiers -in HTML and DocBook output, and to footnote numbers in Markdown output. +Specify a prefix to be added to all identifiers and internal links in +HTML and DocBook output, and to footnote numbers in Markdown and Haddock +output. This is useful for preventing duplicate identifiers when generating fragments to be included in other pages. .RS @@ -887,29 +940,20 @@ Link to a CSS style sheet. This option can be used repeatedly to include multiple files. They will be included in the order specified. .RS +.PP +A stylesheet is required for generating EPUB. +If none is provided using this option (or the \f[C]stylesheet\f[] +metadata field), pandoc will look for a file \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. .RE .TP -.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. -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[]). -If this is not found either, sensible defaults will be used. +.B \f[C]\-\-reference\-doc=\f[]\f[I]FILE\f[] +Use the specified file as a style reference in producing a docx or ODT +file. .RS -.PP -To produce a custom \f[C]reference.odt\f[], first get a copy of the -default \f[C]reference.odt\f[]: -\f[C]pandoc\ \-\-print\-default\-data\-file\ reference.odt\ >\ custom\-reference.odt\f[]. -Then open \f[C]custom\-reference.docx\f[] in LibreOffice, modify the -styles as you wish, and save the file. -.RE .TP -.B \f[C]\-\-reference\-docx=\f[]\f[I]FILE\f[] -Use the specified file as a style reference in producing a docx file. +.B Docx For best results, the reference docx should be a modified version of a docx file produced using pandoc. The contents of the reference docx are ignored, but its stylesheets and @@ -930,19 +974,30 @@ For best results, do not make changes to this file other than modifying the styles used by pandoc: [paragraph] Normal, Body Text, First Paragraph, Compact, Title, Subtitle, Author, Date, Abstract, Bibliography, Heading 1, Heading 2, Heading 3, Heading 4, Heading 5, -Heading 6, Block Text, Footnote Text, Definition Term, Definition, -Caption, Table Caption, Image Caption, Figure, Figure With Caption, TOC -Heading; [character] Default Paragraph Font, Body Text Char, Verbatim -Char, Footnote Reference, Hyperlink; [table] Normal Table. +Heading 6, Heading 7, Heading 8, Heading 9, Block Text, Footnote Text, +Definition Term, Definition, Caption, Table Caption, Image Caption, +Figure, Captioned Figure, TOC Heading; [character] Default Paragraph +Font, Body Text Char, Verbatim Char, Footnote Reference, Hyperlink; +[table] Table. .RE .TP -.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 +.B ODT +For best results, the reference ODT should be a modified version of an +ODT produced using pandoc. +The contents of the reference ODT are ignored, but its stylesheets are +used in the new ODT. +If no reference ODT is specified on the command line, pandoc will look +for a file \f[C]reference.odt\f[] in the user data directory (see \f[C]\-\-data\-dir\f[]). -If it is not found there, sensible defaults will be used. +If this is not found either, sensible defaults will be used. .RS +.PP +To produce a custom \f[C]reference.odt\f[], first get a copy of the +default \f[C]reference.odt\f[]: +\f[C]pandoc\ \-\-print\-default\-data\-file\ reference.odt\ >\ custom\-reference.odt\f[]. +Then open \f[C]custom\-reference.odt\f[] in LibreOffice, modify the +styles as you wish, and save the file. +.RE .RE .TP .B \f[C]\-\-epub\-cover\-image=\f[]\f[I]FILE\f[] @@ -989,7 +1044,7 @@ However, if you use wildcards on the command line, be sure to escape them or put the whole filename in single quotes, to prevent them from being interpreted by the shell. To use the embedded fonts, you will need to add declarations like the -following to your CSS (see \f[C]\-\-epub\-stylesheet\f[]): +following to your CSS (see \f[C]\-\-css\f[]): .RS .IP .nf @@ -1115,12 +1170,9 @@ copy of the script, so it can be cached. .RS .RE .TP -.B \f[C]\-\-mathml\f[][\f[C]=\f[]\f[I]URL\f[]] -Convert TeX math to MathML (in \f[C]docbook\f[], \f[C]docbook5\f[], -\f[C]html\f[] and \f[C]html5\f[]). -In standalone \f[C]html\f[] output, a small JavaScript (or a link to -such a script if a \f[I]URL\f[] is supplied) will be inserted that -allows the MathML to be viewed on some browsers. +.B \f[C]\-\-mathml\f[] +Convert TeX math to MathML (in \f[C]docbook4\f[], \f[C]docbook5\f[], +\f[C]jats\f[], \f[C]html4\f[] and \f[C]html5\f[]). .RS .RE .TP @@ -1252,7 +1304,7 @@ For \f[C]pdf\f[] output, customize the \f[C]default.latex\f[] template use \f[C]\-t\ context\f[]). .IP \[bu] 2 \f[C]docx\f[] has no template (however, you can use -\f[C]\-\-reference\-docx\f[] to customize the output). +\f[C]\-\-reference\-doc\f[] to customize the output). .PP Templates contain \f[I]variables\f[], which allow for the inclusion of arbitrary information at any point in the file. @@ -1266,6 +1318,29 @@ Some variables are set automatically by pandoc. These vary somewhat depending on the output format, but include metadata fields as well as the following: .TP +.B \f[C]sourcefile\f[], \f[C]outputfile\f[] +source and destination filenames, as given on the command line. +\f[C]sourcefile\f[] can also be a list if input comes from multiple +files, or empty if input is from stdin. +You can use the following snippet in your template to distinguish them: +.RS +.IP +.nf +\f[C] +$if(sourcefile)$ +$for(sourcefile)$ +$sourcefile$ +$endfor$ +$else$ +(stdin) +$endif$ +\f[] +.fi +.PP +Similarly, \f[C]outputfile\f[] can be \f[C]\-\f[] if output goes to the +terminal. +.RE +.TP .B \f[C]title\f[], \f[C]author\f[], \f[C]date\f[] allow identification of basic aspects of the document. Included in PDF metadata through LaTeX and ConTeXt. @@ -1344,7 +1419,8 @@ body of document .RE .TP .B \f[C]meta\-json\f[] -JSON representation of all of the document's metadata +JSON representation of all of the document's metadata. +Field values are transformed to the selected output format. .RS .RE .SS Language variables @@ -1398,6 +1474,16 @@ engine is fully supported (use \f[C]\-\-latex\-engine=xelatex\f[]). Variables are available for producing slide shows with pandoc, including all reveal.js configuration options. .TP +.B \f[C]titlegraphic\f[] +title graphic for Beamer documents +.RS +.RE +.TP +.B \f[C]logo\f[] +logo for Beamer documents +.RS +.RE +.TP .B \f[C]slidy\-url\f[] base URL for Slidy documents (defaults to \f[C]http://www.w3.org/Talks/Tools/Slidy2\f[]) @@ -1467,7 +1553,7 @@ LaTeX variables are used when creating a PDF. .TP .B \f[C]papersize\f[] paper size, e.g. -\f[C]letter\f[], \f[C]A4\f[] +\f[C]letter\f[], \f[C]a4\f[] .RS .RE .TP @@ -1745,6 +1831,31 @@ adjusts text to left (\f[C]l\f[]), right (\f[C]r\f[]), center if \f[C]true\f[] (the default), hyphenation will be used .RS .RE +.SS Variables for ms +.TP +.B \f[C]pointsize\f[] +point size (e.g. +\f[C]10p\f[]) +.RS +.RE +.TP +.B \f[C]lineheight\f[] +line height (e.g. +\f[C]12p\f[]) +.RS +.RE +.TP +.B \f[C]fontfamily\f[] +font family (e.g. +\f[C]T\f[] or \f[C]P\f[]) +.RS +.RE +.TP +.B \f[C]indent\f[] +paragraph indent (e.g. +\f[C]2m\f[]) +.RS +.RE .SS Using variables in templates .PP Variable names are sequences of alphanumerics, \f[C]\-\f[], and @@ -1925,6 +2036,12 @@ I\ like\ several\ of\ their\ flavors\ of\ ice\ cream: #22,\ for\ example,\ and\ #5. \f[] .fi +.SS Extension: \f[C]space_in_atx_header\f[] +.PP +Many Markdown implementations do not require a space between the opening +\f[C]#\f[]s of an ATX header and the header text, so that +\f[C]#5\ bolt\f[] and \f[C]#hashtag\f[] count as headers. +With this extension, pandoc does require the space. .SS Header identifiers .SS Extension: \f[C]header_attributes\f[] .PP @@ -2065,9 +2182,9 @@ only in HTML, LaTeX, and ConTeXt formats. .PP 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. .SS Extension: \f[C]implicit_header_references\f[] @@ -2285,10 +2402,10 @@ Here \f[C]mycode\f[] is an identifier, \f[C]haskell\f[] and \f[C]numberLines\f[] are classes, and \f[C]startFrom\f[] is an attribute with value \f[C]100\f[]. Some output formats can use this information to do syntax highlighting. -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. +Currently, the only output formats that uses this information are HTML, +LaTeX, Docx, and Ms.\ If highlighting is supported for your output +format and language, then the code block above will appear highlighted, +with numbered lines. (To see which languages are supported, type \f[C]pandoc\ \-\-list\-highlight\-languages\f[].) Otherwise, the code block above will appear as follows: @@ -3164,7 +3281,8 @@ Note that YAML escaping rules must be followed. Thus, for example, if a title contains a colon, it must be quoted. The pipe character (\f[C]|\f[]) can be used to begin an indented block that will be interpreted literally, without need for escaping. -This form is necessary when the field contains blank lines: +This form is necessary when the field contains blank lines or +block\-level formatting: .IP .nf \f[C] @@ -3283,19 +3401,6 @@ This is a nice alternative to Markdown's \[lq]invisible\[rq] way of indicating hard line breaks using two trailing spaces on a line. .PP Backslash escapes do not work in verbatim contexts. -.SS Smart punctuation -.SS Extension -.PP -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 -\f[C]\&...\f[] to ellipses. -Nonbreaking spaces are inserted after certain abbreviations, such as -\[lq]Mr.\[rq] -.PP -Note: if your LaTeX template or any included header file call for the -\f[C]csquotes\f[] package, pandoc will detect this automatically and use -\f[C]\\enquote{...}\f[] for quoted text. .SS Inline formatting .SS Emphasis .PP @@ -3560,7 +3665,9 @@ 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 CodeCogs will be used +For SVG images you can for example use +\f[C]\-\-webtex\ https://latex.codecogs.com/svg.latex?\f[]. +If no URL is specified, the CodeCogs URL generating PNGs will be used (\f[C]https://latex.codecogs.com/png.latex?\f[]). .IP "7." 3 If the \f[C]\-\-mathjax\f[] option is used, TeX math will be displayed @@ -3742,10 +3849,11 @@ 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 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 cannot be space between the two unless the +\f[C]spaced_reference_links\f[] extension is enabled.) 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. The label must not be parseable as a citation (assuming the \f[C]citations\f[] extension is enabled): citations take precedence over link labels. @@ -3887,6 +3995,10 @@ One way to do this is to insert a nonbreaking space after the image: ![This\ image\ won\[aq]t\ be\ a\ figure](/url/of/image.png)\\\ \f[] .fi +.PP +Note that in reveal.js slide shows, an image in a paragraph by itself +that has the \f[C]stretch\f[] class will fill the screen, and the +caption and figure tags will be omitted. .SS Extension: \f[C]link_attributes\f[] .PP Attributes can be set on links and images: @@ -4006,6 +4118,31 @@ note.] .fi .PP Inline and regular footnotes may be mixed freely. +.SS Typography +.SS Extension: \f[C]smart\f[] +.PP +Interpret straight quotes as curly quotes, \f[C]\-\-\-\f[] as +em\-dashes, \f[C]\-\-\f[] as en\-dashes, and \f[C]\&...\f[] as ellipses. +Nonbreaking spaces are inserted after certain abbreviations, such as +\[lq]Mr.\[rq] This option currently affects the input formats +\f[C]markdown\f[], \f[C]commonmark\f[], \f[C]latex\f[], +\f[C]mediawiki\f[], \f[C]org\f[], \f[C]rst\f[], and \f[C]twiki\f[], and +the output formats \f[C]markdown\f[], \f[C]latex\f[], and +\f[C]context\f[]. +.PP +Note: If you are \f[I]writing\f[] Markdown, then the \f[C]smart\f[] +extension has the reverse effect: what would have been curly quotes +comes out straight. +.PP +In LaTeX, \f[C]smart\f[] means to use the standard TeX ligatures for +quotation marks (\f[C]``\f[] and \f[C]\[aq]\[aq]\f[] for double quotes, +\f[C]`\f[] and \f[C]\[aq]\f[] for single quotes) and dashes +(\f[C]\-\-\f[] for en\-dash and \f[C]\-\-\-\f[] for em\-dash). +If \f[C]smart\f[] is disabled, then in reading LaTeX pandoc will parse +these characters literally. +In writing LaTeX, enabling \f[C]smart\f[] tells pandoc to use the +ligatures when possible; if \f[C]smart\f[] is disabled pandoc will use +unicode quotation mark and dash characters. .SS Citations .SS Extension: \f[C]citations\f[] .PP @@ -4311,6 +4448,13 @@ 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. +.SS Extension: \f[C]old_dashes\f[] +.PP +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. +This option only has an effect if \f[C]smart\f[] is enabled. +It is selected automatically for \f[C]textile\f[] input. .SS Extension: \f[C]angle_brackets_escapable\f[] .PP Allow \f[C]<\f[] and \f[C]>\f[] to be backslash\-escaped, as they can be @@ -4320,6 +4464,16 @@ This is implied by pandoc's default \f[C]all_symbols_escapable\f[]. .PP Allow a list to occur right after a paragraph, with no intervening blank space. +.SS Extension: \f[C]spaced_reference_links\f[] +.PP +Allow whitespace between the two components of a reference link, for +example, +.IP +.nf +\f[C] +[foo]\ [bar]. +\f[] +.fi .SS Extension: \f[C]hard_line_breaks\f[] .PP Causes all newlines within a paragraph to be interpreted as hard line @@ -4593,6 +4747,9 @@ Headers \f[I]above\f[] the slide level in the hierarchy create \[lq]title slides,\[rq] which just contain the section title and help to break the slide show into sections. .IP \[bu] 2 +Content \f[I]above\f[] the slide level will not appear in the slide +show. +.IP \[bu] 2 A title page is constructed automatically from the document's title block, if present. (In the case of beamer, this can be disabled by commenting out some @@ -4887,7 +5044,7 @@ For example: 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]markdown_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 +output; \f[C]beamer\f[], \f[C]html4\f[] or \f[C]html5\f[] for output only), pandoc will treat the document as literate Haskell source. This means that .IP \[bu] 2 @@ -4940,12 +5097,16 @@ pandoc\ \-f\ markdown+lhs\ \-t\ html+lhs .PP writes HTML with the Haskell code in bird tracks, so it can be copied and pasted as literate Haskell source. +.PP +Note that GHC expects the bird tracks in the first column, so indentend +literate code blocks (e.g.\ inside an itemized environment) will not be +picked up by the Haskell compiler. .SH SYNTAX HIGHLIGHTING .PP Pandoc will automatically highlight syntax in fenced code blocks that are marked with a language name. -The Haskell library highlighting\-kate is used for highlighting, which -works in HTML, Docx, and LaTeX/PDF output. +The Haskell library skylighting is used for highlighting, which works in +HTML, Docx, Ms, and LaTeX/PDF output. To see a list of language names that pandoc will recognize, type \f[C]pandoc\ \-\-list\-highlight\-languages\f[]. .PP @@ -5038,7 +5199,7 @@ pandoc\ \-\-print\-default\-data\-file\ sample.lua .fi .SH AUTHORS .PP -© 2006\-2016 John MacFarlane (jgm\@berkeley.edu). +© 2006\-2017 John MacFarlane (jgm\@berkeley.edu). Released under the GPL, version 2 or greater. This software carries no warranty of any kind. (See COPYRIGHT for full copyright and warranty notices.) diff --git a/pandoc.cabal b/pandoc.cabal index f9ce1efd2..8d336097d 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -136,8 +136,15 @@ Extra-Source-Files: test/*.native test/command/*.md test/command/abbrevs + test/command/SVG_logo-without-xml-declaration.svg + test/command/SVG_logo.svg + test/command/corrupt.svg + test/command/inkscape-cube.svg test/command/sub-file-chapter-1.tex test/command/sub-file-chapter-2.tex + test/command/3510-subdoc.org + test/command/3510-export.latex + test/command/3510-src.hs test/docbook-reader.docbook test/docbook-xref.docbook test/html-reader.html @@ -264,10 +271,6 @@ Flag weigh-pandoc Description: Build weigh-pandoc to measure memory usage. Default: False -Flag https - Description: Enable support for downloading of resources over https. - Default: True - Flag network-uri Description: Get Network.URI from the network-uri package Default: True @@ -316,7 +319,10 @@ Library JuicyPixels >= 3.1.6.1 && < 3.3, Glob >= 0.7 && < 0.8, cmark >= 0.5 && < 0.6, - doctemplates >= 0.1 && < 0.2 + doctemplates >= 0.1 && < 0.2, + http-client >= 0.4.30 && < 0.6, + http-client-tls >= 0.2.4 && < 0.4, + http-types >= 0.8 && < 0.10 if os(windows) Cpp-options: -D_WINDOWS else @@ -330,18 +336,10 @@ Library Build-Depends: network-uri >= 2.6 && < 2.7, network >= 2.6 else Build-Depends: network >= 2 && < 2.6 - if flag(https) - Build-Depends: http-client >= 0.4.30 && < 0.6, - http-client-tls >= 0.2.4 && < 0.4, - http-types >= 0.8 && < 0.10 - cpp-options: -DHTTP_CLIENT if flag(embed_data_files) cpp-options: -DEMBED_DATA_FILES build-depends: file-embed >= 0.0 && < 0.1 other-modules: Text.Pandoc.Data - if os(darwin) - Build-Tools: cpphs >= 1.19 - ghc-options: -pgmP cpphs -optP --cpp if os(windows) Cpp-options: -D_WINDOWS Ghc-Options: -Wall -fno-warn-unused-do-bind @@ -448,6 +446,7 @@ Library Text.Pandoc.Readers.Odt.Arrows.Utils, Text.Pandoc.Readers.Org.BlockStarts, Text.Pandoc.Readers.Org.Blocks, + Text.Pandoc.Readers.Org.DocumentTree, Text.Pandoc.Readers.Org.ExportSettings, Text.Pandoc.Readers.Org.Inlines, Text.Pandoc.Readers.Org.Meta, @@ -508,6 +507,7 @@ Executable weigh-pandoc if flag(weigh-pandoc) Build-Depends: pandoc, base >= 4.2 && < 5, + text, weigh >= 0.0 && < 0.1, mtl >= 2.2 && < 2.3 Buildable: True @@ -585,7 +585,8 @@ benchmark benchmark-pandoc Build-Depends: pandoc, time, bytestring, containers, base >= 4.2 && < 5, + text >= 0.11 && < 1.3, syb >= 0.1 && < 0.8, - criterion >= 1.0 && < 1.2 + criterion >= 1.0 && < 1.3 Ghc-Options: -rtsopts -Wall -fno-warn-unused-do-bind Default-Language: Haskell98 @@ -1,5 +1,5 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE PatternGuards #-} + {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- @@ -33,10 +33,11 @@ Parses command-line options and calls the appropriate readers and writers. -} module Main where -import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions) -import Text.Pandoc.Error (handleError, PandocError) import qualified Control.Exception as E +import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions) +import Text.Pandoc.Error (PandocError, handleError) main :: IO () main = E.catch (parseOptions options defaultOpts >>= convertWithOpts) (\(e :: PandocError) -> handleError (Left e)) + diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 345ef3b18..8ee1adf13 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -2,7 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c38ebdd84..19066e8b7 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1,8 +1,9 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.App - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> @@ -40,45 +41,50 @@ module Text.Pandoc.App ( import Control.Applicative ((<|>)) import qualified Control.Exception as E import Control.Monad +import Control.Monad.Except (throwError) import Control.Monad.Trans -import Data.Aeson (eitherDecode', encode) +import Data.Monoid +import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', + encode, genericToEncoding) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) -import qualified Data.Set as Set import Data.Foldable (foldrM) import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) +import qualified Data.Set as Set +import Data.Text (Text) import qualified Data.Text as T import Data.Yaml (decode) import qualified Data.Yaml as Yaml -import Network.URI (URI (..), isURI, parseURI) +import GHC.Generics +import Network.URI (URI (..), parseURI) import Paths_pandoc (getDataDir) import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme) -import Skylighting.Parser (missingIncludes, parseSyntaxDefinition, - addSyntaxDefinition) +import Skylighting.Parser (addSyntaxDefinition, missingIncludes, + parseSyntaxDefinition) import System.Console.GetOpt import System.Directory (Permissions (..), doesFileExist, findExecutable, getAppUserDataDirectory, getPermissions) import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath -import System.IO (stdout) +import System.IO (nativeNewline, stdout) +import qualified System.IO as IO (Newline (..)) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, getLog, withMediaBag) +import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, + setResourcePath, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.Lua ( runLuaFilter ) -import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory) +import Text.Pandoc.Lua (runLuaFilter) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) -import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) -import Text.Pandoc.Shared (headerShift, openURL, readDataFile, +import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) +import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.Walk (walk) import Text.Pandoc.XML (toEntities) import Text.Printf #ifndef _WINDOWS @@ -86,6 +92,12 @@ import System.Posix.IO (stdOutput) import System.Posix.Terminal (queryTerminal) #endif +data LineEnding = LF | CRLF | Native deriving (Show, Generic) + +instance ToJSON LineEnding where + toEncoding = genericToEncoding defaultOptions +instance FromJSON LineEnding + parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt parseOptions options' defaults = do rawArgs <- map UTF8.decodeArg <$> getArgs @@ -171,7 +183,7 @@ convertWithOpts opts = do -- disabling the custom writer for now writer <- if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName - then return (StringWriter + then return (TextWriter (\o d -> liftIO $ writeCustom writerName o d) :: Writer PandocIO) else case getWriter writerName of @@ -233,10 +245,9 @@ convertWithOpts opts = do withList f (x:xs) vars = f x vars >>= withList f xs variables <- - return (("outputfile", optOutputFile opts) : optVariables opts) - >>= + withList (addStringAsVariable "sourcefile") - (reverse $ optInputFiles opts) + (reverse $ optInputFiles opts) (("outputfile", optOutputFile opts) : optVariables opts) -- we reverse this list because, unlike -- the other option lists here, it is -- not reversed when parsed from CLI arguments. @@ -372,8 +383,8 @@ convertWithOpts opts = do then 0 else optTabStop opts) - readSources :: (Functor m, MonadIO m) => [FilePath] -> m String - readSources srcs = convertTabs . intercalate "\n" <$> + readSources :: [FilePath] -> PandocIO Text + readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$> mapM readSource srcs let runIO' :: PandocIO a -> IO a @@ -391,42 +402,47 @@ convertWithOpts opts = do E.throwIO PandocFailOnWarningError return res - let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag) + let sourceToDoc :: [FilePath] -> PandocIO Pandoc sourceToDoc sources' = case reader of - StringReader r - | optFileScope opts || readerName == "json" -> do - pairs <- mapM - (readSource >=> withMediaBag . r readerOpts) sources - return (mconcat (map fst pairs), mconcat (map snd pairs)) + TextReader r + | optFileScope opts || readerName == "json" -> + mconcat <$> mapM (readSource >=> r readerOpts) sources | otherwise -> - readSources sources' >>= withMediaBag . r readerOpts - ByteStringReader r -> do - pairs <- mapM (readFile' >=> - withMediaBag . r readerOpts) sources - return (mconcat (map fst pairs), mconcat (map snd pairs)) + readSources sources' >>= r readerOpts + ByteStringReader r -> + mconcat <$> mapM (readFile' >=> r readerOpts) sources metadata <- if format == "jats" && - lookup "csl" (optMetadata opts) == Nothing && - lookup "citation-style" (optMetadata opts) == Nothing + isNothing (lookup "csl" (optMetadata opts)) && + isNothing (lookup "citation-style" (optMetadata opts)) then do jatsCSL <- readDataFile datadir "jats.csl" let jatsEncoded = makeDataURI ("application/xml", jatsCSL) return $ ("csl", jatsEncoded) : optMetadata opts else return $ optMetadata opts + let eol = case optEol opts of + CRLF -> IO.CRLF + LF -> IO.LF + Native -> nativeNewline + runIO' $ do - (doc, media) <- sourceToDoc sources - doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=> - return . flip (foldr addMetadata) metadata >=> - applyTransforms transforms >=> - applyLuaFilters datadir (optLuaFilters opts) [format] >=> - applyFilters datadir filters' [format]) doc + setResourcePath (optResourcePath opts) + (doc, media) <- withMediaBag $ sourceToDoc sources >>= + ( (if isJust (optExtractMedia opts) + then fillMediaBag (writerSourceURL writerOptions) + else return) + >=> maybe return extractMedia (optExtractMedia opts) + >=> return . flip (foldr addMetadata) metadata + >=> applyTransforms transforms + >=> applyLuaFilters datadir (optLuaFilters opts) [format] + >=> applyFilters datadir filters' [format] + ) case writer of - -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile - ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile - StringWriter f + ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile + TextWriter f | pdfOutput -> do -- make sure writer is latex, beamer, context, html5 or ms unless (laTeXOutput || conTeXtOutput || html5Output || @@ -445,7 +461,7 @@ convertWithOpts opts = do when (isNothing mbPdfProg) $ liftIO $ E.throwIO $ PandocPDFProgramNotFoundError pdfprog - res <- makePDF pdfprog f writerOptions verbosity media doc' + res <- makePDF pdfprog f writerOptions verbosity media doc case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ @@ -453,18 +469,23 @@ convertWithOpts opts = do | otherwise -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] - selfcontain = if optSelfContained opts && htmlFormat - then makeSelfContained writerOptions - else return handleEntities = if (htmlFormat || format == "docbook4" || format == "docbook5" || format == "docbook") && optAscii opts then toEntities else id - output <- f writerOptions doc' - selfcontain (output ++ ['\n' | not standalone]) >>= - writerFn outputFile . handleEntities + addNl = if standalone + then id + else (<> T.singleton '\n') + output <- (addNl . handleEntities) <$> f writerOptions doc + writerFn eol outputFile =<< + if optSelfContained opts && htmlFormat + -- TODO not maximally efficient; change type + -- of makeSelfContained so it works w/ Text + then T.pack <$> makeSelfContained writerOptions + (T.unpack output) + else return output type Transform = Pandoc -> Pandoc @@ -568,7 +589,13 @@ data Opt = Opt , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header - } + , optResourcePath :: [FilePath] -- ^ Path to search for images etc + , optEol :: LineEnding -- ^ Style of line-endings to use + } deriving (Generic, Show) + +instance ToJSON Opt where + toEncoding = genericToEncoding defaultOptions +instance FromJSON Opt -- | Defaults for command-line options. defaultOpts :: Opt @@ -636,6 +663,8 @@ defaultOpts = Opt , optIncludeBeforeBody = [] , optIncludeAfterBody = [] , optIncludeInHeader = [] + , optResourcePath = ["."] + , optEol = Native } addMetadata :: (String, String) -> Pandoc -> Pandoc @@ -728,19 +757,6 @@ defaultWriterName x = -- Transformations of a Pandoc document post-parsing: -extractMedia :: MonadIO m => MediaBag -> FilePath -> Pandoc -> m Pandoc -extractMedia media dir d = - case [fp | (fp, _, _) <- mediaDirectory media] of - [] -> return d - fps -> do - extractMediaBag True dir media - return $ walk (adjustImagePath dir fps) d - -adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline -adjustImagePath dir paths (Image attr lab (src, tit)) - | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) -adjustImagePath _ _ x = x - applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc applyTransforms transforms d = return $ foldr ($) d transforms @@ -773,17 +789,23 @@ applyFilters mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters foldrM ($) d $ map (flip externalFilter args) expandedFilters -readSource :: MonadIO m => FilePath -> m String -readSource "-" = liftIO UTF8.getContents +readSource :: FilePath -> PandocIO Text +readSource "-" = liftIO (UTF8.toText <$> BS.getContents) readSource src = case parseURI src of Just u | uriScheme u `elem` ["http:","https:"] -> readURI src | uriScheme u == "file:" -> - liftIO $ UTF8.readFile (uriPath u) - _ -> liftIO $ UTF8.readFile src - -readURI :: MonadIO m => FilePath -> m String -readURI src = liftIO $ (UTF8.toString . fst) <$> openURL src + liftIO $ UTF8.toText <$> + BS.readFile (uriPath u) + _ -> liftIO $ UTF8.toText <$> + BS.readFile src + +readURI :: FilePath -> PandocIO Text +readURI src = do + res <- liftIO $ openURL src + case res of + Left e -> throwError $ PandocHttpError src e + Right (contents, _) -> return $ UTF8.toText contents readFile' :: MonadIO m => FilePath -> m B.ByteString readFile' "-" = liftIO B.getContents @@ -793,9 +815,10 @@ writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m () writeFnBinary "-" = liftIO . B.putStr writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f) -writerFn :: MonadIO m => FilePath -> String -> m () -writerFn "-" = liftIO . UTF8.putStr -writerFn f = liftIO . UTF8.writeFile f +writerFn :: MonadIO m => IO.Newline -> FilePath -> Text -> m () +-- TODO this implementation isn't maximally efficient: +writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack +writerFn eol f = liftIO . UTF8.writeFileWith eol f . T.unpack lookupHighlightStyle :: Maybe String -> IO (Maybe Style) lookupHighlightStyle Nothing = return Nothing @@ -968,6 +991,19 @@ options = "NUMBER") "" -- "Dpi (default 96)" + , Option "" ["eol"] + (ReqArg + (\arg opt -> + case toLower <$> arg of + "crlf" -> return opt { optEol = CRLF } + "lf" -> return opt { optEol = LF } + "native" -> return opt { optEol = Native } + -- mac-syntax (cr) is not supported in ghc-base. + _ -> E.throwIO $ PandocOptionError + "--eol must be crlf, lf, or native") + "crlf|lf|native") + "" -- "EOL (default OS-dependent)" + , Option "" ["wrap"] (ReqArg (\arg opt -> @@ -1046,6 +1082,14 @@ options = "FILE") "" -- "File to include after document body" + , Option "" ["resource-path"] + (ReqArg + (\arg opt -> return opt { optResourcePath = + splitSearchPath arg }) + "SEARCHPATH") + "" -- "Paths to search for images and other resources" + + , Option "" ["self-contained"] (NoArg (\opt -> return opt { optSelfContained = True, @@ -1388,8 +1432,8 @@ options = map ("--" ++) longs let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ printf tpl allopts - (unwords readers'names) - (unwords writers'names) + (unwords readersNames) + (unwords writersNames) (unwords $ map fst highlightingStyles) ddir exitSuccess )) @@ -1398,14 +1442,14 @@ options = , Option "" ["list-input-formats"] (NoArg (\_ -> do - mapM_ (UTF8.hPutStrLn stdout) readers'names + mapM_ (UTF8.hPutStrLn stdout) readersNames exitSuccess )) "" , Option "" ["list-output-formats"] (NoArg (\_ -> do - mapM_ (UTF8.hPutStrLn stdout) writers'names + mapM_ (UTF8.hPutStrLn stdout) writersNames exitSuccess )) "" @@ -1509,14 +1553,15 @@ uppercaseFirstLetter :: String -> String uppercaseFirstLetter (c:cs) = toUpper c : cs uppercaseFirstLetter [] = [] -readers'names :: [String] -readers'names = sort (map fst (readers :: [(String, Reader PandocIO)])) +readersNames :: [String] +readersNames = sort (map fst (readers :: [(String, Reader PandocIO)])) -writers'names :: [String] -writers'names = sort (map fst (writers :: [(String, Writer PandocIO)])) +writersNames :: [String] +writersNames = sort (map fst (writers :: [(String, Writer PandocIO)])) splitField :: String -> (String, String) splitField s = case break (`elem` ":=") s of (k,_:v) -> (k,v) (k,[]) -> (k,"true") + diff --git a/src/Text/Pandoc/Asciify.hs b/src/Text/Pandoc/Asciify.hs index 411a112b2..7125e5bcd 100644 --- a/src/Text/Pandoc/Asciify.hs +++ b/src/Text/Pandoc/Asciify.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Asciify - Copyright : Copyright (C) 2013-2016 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/CSS.hs b/src/Text/Pandoc/CSS.hs index 3e2fd6309..41be1ea13 100644 --- a/src/Text/Pandoc/CSS.hs +++ b/src/Text/Pandoc/CSS.hs @@ -11,7 +11,7 @@ import Text.Parsec.String ruleParser :: Parser (String, String) ruleParser = do p <- many1 (noneOf ":") <* char ':' - v <- many1 (noneOf ":;") <* (optional $ char ';') <* spaces + v <- many1 (noneOf ":;") <* optional (char ';') <* spaces return (trim p, trim v) styleAttrParser :: Parser [(String, String)] diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 1afa64c10..14a0b8044 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,6 +1,8 @@ -{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, -FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, -StandaloneDeriving #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE FlexibleContexts #-} {- Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -61,6 +63,8 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIOorExplode , runPure , withMediaBag + , fillMediaBag + , extractMedia ) where import Prelude hiding (readFile) @@ -76,8 +80,11 @@ import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Logging import Text.Parsec (ParsecT) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) -import Text.Pandoc.MIME (MimeType, getMimeType) +import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) +import Text.Pandoc.Definition import Data.Char (toLower) +import Data.Digest.Pure.SHA (sha1, showDigest) +import Data.Maybe (fromMaybe) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) @@ -86,13 +93,16 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) +import Text.Pandoc.Walk (walkM, walk) import qualified Text.Pandoc.MediaBag as MB import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.FilePath ((</>), takeExtension, dropExtension, isRelative) +import System.Directory (createDirectoryIfMissing) +import System.FilePath ((</>), (<.>), takeDirectory, + takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) @@ -145,7 +155,7 @@ report :: PandocMonad m => LogMessage -> m () report msg = do verbosity <- getsCommonState stVerbosity let level = messageVerbosity msg - when (level <= verbosity) $ do + when (level <= verbosity) $ logOutput msg unless (level == DEBUG) $ modifyCommonState $ \st -> st{ stLog = msg : stLog st } @@ -213,7 +223,7 @@ runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) -withMediaBag ma = ((,)) <$> ma <*> getMediaBag +withMediaBag ma = (,) <$> ma <*> getMediaBag runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError @@ -239,10 +249,13 @@ instance PandocMonad PandocIO where getCurrentTime = liftIO IO.getCurrentTime getCurrentTimeZone = liftIO IO.getCurrentTimeZone newStdGen = liftIO IO.newStdGen - newUniqueHash = hashUnique <$> (liftIO IO.newUnique) + newUniqueHash = hashUnique <$> liftIO IO.newUnique openURL u = do report $ Fetching u - liftIOError IO.openURL u + res <- liftIO (IO.openURL u) + case res of + Right r -> return r + Left e -> throwError $ PandocHttpError u e readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s readDataFile mfp fname = liftIOError (IO.readDataFile mfp) fname @@ -252,7 +265,7 @@ instance PandocMonad PandocIO where putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do UTF8.hPutStr stderr $ "[" ++ - (map toLower $ show (messageVerbosity msg)) ++ "] " + map toLower (show (messageVerbosity msg)) ++ "] " alertIndent $ lines $ showLogMessage msg alertIndent :: [String] -> IO () @@ -283,14 +296,14 @@ fetchItem :: PandocMonad m fetchItem sourceURL s = do mediabag <- getMediaBag case lookupMedia s mediabag of - Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) + Just (mime, bs) -> return (BL.toStrict bs, Just mime) Nothing -> downloadOrRead sourceURL s downloadOrRead :: PandocMonad m => Maybe String -> String -> m (B.ByteString, Maybe MimeType) -downloadOrRead sourceURL s = do +downloadOrRead sourceURL s = case (sourceURL >>= parseURIReference' . ensureEscaped, ensureEscaped s) of (Just u, s') -> -- try fetching from relative path at source @@ -330,12 +343,73 @@ downloadOrRead sourceURL s = do convertSlash x = x withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a -withPaths [] _ fp = throwError $ PandocIOError fp - (userError "file not found in resource path") +withPaths [] _ fp = throwError $ PandocResourceNotFound fp withPaths (p:ps) action fp = catchError (action (p </> fp)) (\_ -> withPaths ps action fp) +-- | Traverse tree, filling media bag for any images that +-- aren't already in the media bag. +fillMediaBag :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc +fillMediaBag sourceURL d = walkM handleImage d + where handleImage :: PandocMonad m => Inline -> m Inline + handleImage (Image attr lab (src, tit)) = catchError + (do mediabag <- getMediaBag + case lookupMedia src mediabag of + Just (_, _) -> return $ Image attr lab (src, tit) + Nothing -> do + (bs, mt) <- downloadOrRead sourceURL src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = BL.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + insertMedia fname mt bs' + return $ Image attr lab (fname, tit)) + (\e -> + case e of + PandocResourceNotFound _ -> do + report $ CouldNotFetchResource src + "replacing image with description" + -- emit alt text + return $ Span ("",["image"],[]) lab + PandocHttpError u er -> do + report $ CouldNotFetchResource u + (show er ++ "\rReplacing image with description.") + -- emit alt text + return $ Span ("",["image"],[]) lab + _ -> throwError e) + handleImage x = return x + +-- | Extract media from the mediabag into a directory. +extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc +extractMedia dir d = do + media <- getMediaBag + case [fp | (fp, _, _) <- mediaDirectory media] of + [] -> return d + fps -> do + mapM_ (writeMedia dir media) fps + return $ walk (adjustImagePath dir fps) d + +writeMedia :: FilePath -> MediaBag -> FilePath -> PandocIO () +writeMedia dir mediabag subpath = do + -- we join and split to convert a/b/c to a\b\c on Windows; + -- in zip containers all paths use / + let fullpath = dir </> normalise subpath + let mbcontents = lookupMedia subpath mediabag + case mbcontents of + Nothing -> throwError $ PandocResourceNotFound subpath + Just (_, bs) -> do + report $ Extracting fullpath + liftIO $ do + createDirectoryIfMissing True $ takeDirectory fullpath + BL.writeFile fullpath bs + +adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline +adjustImagePath dir paths (Image attr lab (src, tit)) + | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) +adjustImagePath _ _ x = x + data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, @@ -373,7 +447,7 @@ instance Default PureState where getPureState :: PandocPure PureState -getPureState = PandocPure $ lift $ lift $ get +getPureState = PandocPure $ lift $ lift get getsPureState :: (PureState -> a) -> PandocPure a getsPureState f = f <$> getPureState @@ -433,30 +507,27 @@ instance PandocMonad PandocPure where modifyPureState $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - openURL u = throwError $ PandocIOError u $ - userError "Cannot open URL in PandocPure" + openURL u = throwError $ PandocResourceNotFound u readFileLazy fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) - Nothing -> throwError $ PandocIOError fp - (userError "File not found in PureState") + Nothing -> throwError $ PandocResourceNotFound fp readFileStrict fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return bs - Nothing -> throwError $ PandocIOError fp - (userError "File not found in PureState") - readDataFile Nothing "reference.docx" = do + Nothing -> throwError $ PandocResourceNotFound fp + readDataFile Nothing "reference.docx" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx - readDataFile Nothing "reference.odt" = do + readDataFile Nothing "reference.odt" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname readFileStrict fname' readDataFile (Just userDir) fname = do userDirFiles <- getsPureState stUserDataDir - case infoFileContents <$> (getFileInfo (userDir </> fname) userDirFiles) of + case infoFileContents <$> getFileInfo (userDir </> fname) userDirFiles of Just bs -> return bs Nothing -> readDataFile Nothing fname @@ -466,12 +537,12 @@ instance PandocMonad PandocPure where getModificationTime fp = do fps <- getsPureState stFiles - case infoFileMTime <$> (getFileInfo fp fps) of + case infoFileMTime <$> getFileInfo fp fps of Just tm -> return tm Nothing -> throwError $ PandocIOError fp (userError "Can't get modification time") - getCommonState = PandocPure $ lift $ get + getCommonState = PandocPure $ lift get putCommonState x = PandocPure $ lift $ put x logOutput _msg = return () @@ -555,4 +626,3 @@ instance PandocMonad m => PandocMonad (StateT st m) where getCommonState = lift getCommonState putCommonState = lift . putCommonState logOutput = lift . logOutput - diff --git a/src/Text/Pandoc/Compat/Time.hs b/src/Text/Pandoc/Compat/Time.hs index b1cde82a4..1de197801 100644 --- a/src/Text/Pandoc/Compat/Time.hs +++ b/src/Text/Pandoc/Compat/Time.hs @@ -27,4 +27,4 @@ where import Data.Time import System.Locale ( defaultTimeLocale ) -#endif +#endif
\ No newline at end of file diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 135cb3945..3cf381168 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 : Text.Pandoc.Error - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -42,10 +42,12 @@ import Text.Parsec.Pos hiding (Line) import qualified Text.Pandoc.UTF8 as UTF8 import System.Exit (exitWith, ExitCode(..)) import System.IO (stderr) +import Network.HTTP.Client (HttpException) type Input = String data PandocError = PandocIOError String IOError + | PandocHttpError String HttpException | PandocShouldNeverHappenError String | PandocSomeError String | PandocParseError String @@ -58,6 +60,7 @@ data PandocError = PandocIOError String IOError | PandocPDFError String | PandocFilterError String String | PandocCouldNotFindDataFileError String + | PandocResourceNotFound String | PandocAppError String deriving (Show, Typeable, Generic) @@ -69,6 +72,8 @@ handleError (Right r) = return r handleError (Left e) = case e of PandocIOError _ err' -> ioError err' + PandocHttpError u err' -> err 61 $ + "Could not fetch " ++ u ++ "\n" ++ show err' PandocShouldNeverHappenError s -> err 62 s PandocSomeError s -> err 63 s PandocParseError s -> err 64 s @@ -78,7 +83,7 @@ handleError (Left e) = errColumn = sourceColumn errPos ls = lines input ++ [""] errorInFile = if length ls > errLine - 1 - then concat ["\n", (ls !! (errLine - 1)) + then concat ["\n", ls !! (errLine - 1) ,"\n", replicate (errColumn - 1) ' ' ,"^"] else "" @@ -94,6 +99,8 @@ handleError (Left e) = filtername ++ ":\n" ++ msg PandocCouldNotFindDataFileError fn -> err 97 $ "Could not find data file " ++ fn + PandocResourceNotFound fn -> err 99 $ + "File " ++ fn ++ " not found in resource path" PandocAppError s -> err 1 s err :: Int -> String -> IO a diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 24f7d56ec..58e8c414d 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -137,6 +137,7 @@ data Extension = | Ext_shortcut_reference_links -- ^ Shortcut reference links | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. @@ -187,7 +188,7 @@ pandocExtensions = extensionsFromList , Ext_smart ] --- | Extensions to be used with github-flavored markdown. +-- | Extensions to be used with plain text output. plainExtensions :: Extensions plainExtensions = extensionsFromList [ Ext_table_captions @@ -220,6 +221,7 @@ phpMarkdownExtraExtensions = extensionsFromList , Ext_link_attributes , Ext_abbreviations , Ext_shortcut_reference_links + , Ext_spaced_reference_links ] -- | Extensions to be used with github-flavored markdown. @@ -271,6 +273,8 @@ multimarkdownExtensions = extensionsFromList -- not to include these: , Ext_superscript , Ext_subscript + , Ext_backtick_code_blocks + , Ext_spaced_reference_links ] -- | Language extensions to be used with strict markdown. @@ -278,6 +282,7 @@ strictExtensions :: Extensions strictExtensions = extensionsFromList [ Ext_raw_html , Ext_shortcut_reference_links + , Ext_spaced_reference_links ] -- | Default extensions from format-describing string. diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index f249f96ad..0754aae4c 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Highlighting - Copyright : Copyright (C) 2008-2016 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -91,7 +91,7 @@ highlight syntaxmap formatter (_, classes, keyvals) rawCode = , traceOutput = False } classes' = map T.pack classes rawCode' = T.pack rawCode - in case msum (map (\l -> lookupSyntax l syntaxmap) classes') of + in case msum (map ((`lookupSyntax` syntaxmap)) classes') of Nothing | numberLines fmtOpts -> Right $ formatter fmtOpts{ codeClasses = [], @@ -100,9 +100,9 @@ highlight syntaxmap formatter (_, classes, keyvals) rawCode = $ T.lines rawCode' | otherwise -> Left "" Just syntax -> - (formatter fmtOpts{ codeClasses = + formatter fmtOpts{ codeClasses = [T.toLower (sShortname syntax)], - containerClasses = classes' }) <$> + containerClasses = classes' } <$> tokenize tokenizeOpts syntax rawCode' -- Functions for correlating latex listings package's language names diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 8b2d577a9..61ff006cf 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, CPP #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {- - Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2011-2017 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 @@ -20,7 +20,7 @@ {- | Module : Text.Pandoc.ImageSize -Copyright : Copyright (C) 2011-2016 John MacFarlane +Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -43,6 +43,7 @@ module Text.Pandoc.ImageSize ( ImageType(..) , inInch , inPixel , inPoints + , inEm , numUnit , showInInch , showInPixel @@ -80,12 +81,14 @@ data Dimension = Pixel Integer | Centimeter Double | Inch Double | Percent Double + | Em Double instance Show Dimension where show (Pixel a) = show a ++ "px" show (Centimeter a) = showFl a ++ "cm" show (Inch a) = showFl a ++ "in" show (Percent a) = show a ++ "%" + show (Em a) = showFl a ++ "em" data ImageSize = ImageSize{ pxX :: Integer @@ -97,7 +100,13 @@ instance Default ImageSize where def = ImageSize 300 200 72 72 showFl :: (RealFloat a) => a -> String -showFl a = showFFloat (Just 5) a "" +showFl a = removeExtra0s $ showFFloat (Just 5) a "" + +removeExtra0s :: String -> String +removeExtra0s s = + case dropWhile (=='0') $ reverse s of + '.':xs -> reverse xs + xs -> reverse xs imageType :: ByteString -> Maybe ImageType imageType img = case B.take 4 img of @@ -111,12 +120,12 @@ imageType img = case B.take 4 img of | findSvgTag img -> return Svg "%!PS" - | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" + | B.take 4 (B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" -> return Eps _ -> mzero findSvgTag :: ByteString -> Bool -findSvgTag img = B.null $ snd (B.breakSubstring img "<svg") +findSvgTag img = "<svg" `B.isInfixOf` img || "<SVG" `B.isInfixOf` img imageSize :: WriterOptions -> ByteString -> Either String ImageSize imageSize opts img = @@ -159,7 +168,7 @@ desiredSizeInPoints opts attr s = (Nothing, Nothing) -> sizeInPoints s where ratio = fromIntegral (pxX s) / fromIntegral (pxY s) - getDim dir = case (dimension dir attr) of + getDim dir = case dimension dir attr of Just (Percent _) -> Nothing Just dim -> Just $ inPoints opts dim Nothing -> Nothing @@ -167,13 +176,17 @@ desiredSizeInPoints opts attr s = inPoints :: WriterOptions -> Dimension -> Double inPoints opts dim = 72 * inInch opts dim +inEm :: WriterOptions -> Dimension -> Double +inEm opts dim = (64/11) * inInch opts dim + inInch :: WriterOptions -> Dimension -> Double inInch opts dim = case dim of - (Pixel a) -> fromIntegral a / (fromIntegral $ writerDpi opts) + (Pixel a) -> fromIntegral a / fromIntegral (writerDpi opts) (Centimeter a) -> a * 0.3937007874 (Inch a) -> a (Percent _) -> 0 + (Em a) -> a * (11/64) inPixel :: WriterOptions -> Dimension -> Integer inPixel opts dim = @@ -181,7 +194,8 @@ inPixel opts dim = (Pixel a) -> a (Centimeter a) -> floor $ dpi * a * 0.3937007874 :: Integer (Inch a) -> floor $ dpi * a :: Integer - _ -> 0 + (Percent _) -> 0 + (Em a) -> floor $ dpi * a * (11/64) :: Integer where dpi = fromIntegral $ writerDpi opts @@ -213,6 +227,7 @@ scaleDimension factor dim = Centimeter x -> Centimeter (factor * x) Inch x -> Inch (factor * x) Percent x -> Percent (factor * x) + Em x -> Em (factor * x) -- | Read a Dimension from an Attr attribute. -- `dimension Width attr` might return `Just (Pixel 3)` or for example `Just (Centimeter 2.0)`, etc. @@ -236,6 +251,7 @@ lengthToDim s = numUnit s >>= uncurry toDim toDim a "" = Just $ Pixel (floor a::Integer) toDim a "pt" = Just $ Inch (a / 72) toDim a "pc" = Just $ Inch (a / 6) + toDim a "em" = Just $ Em a toDim _ _ = Nothing epsSize :: ByteString -> Maybe ImageSize @@ -245,7 +261,7 @@ epsSize img = do case ls' of [] -> mzero (x:_) -> case B.words x of - (_:_:_:ux:uy:[]) -> do + [_, _, _, ux, uy] -> do ux' <- safeRead $ B.unpack ux uy' <- safeRead $ B.unpack uy return ImageSize{ @@ -263,27 +279,26 @@ pngSize img = do let (i, rest') = B.splitAt 4 $ B.drop 4 rest guard $ i == "MHDR" || i == "IHDR" let (sizes, rest'') = B.splitAt 8 rest' - (x,y) <- case map fromIntegral $ unpack $ sizes of + (x,y) <- case map fromIntegral $unpack sizes of ([w1,w2,w3,w4,h1,h2,h3,h4] :: [Integer]) -> return - ((shift w1 24) + (shift w2 16) + (shift w3 8) + w4, - (shift h1 24) + (shift h2 16) + (shift h3 8) + h4) + (shift w1 24 + shift w2 16 + shift w3 8 + w4, + shift h1 24 + shift h2 16 + shift h3 8 + h4) _ -> Nothing -- "PNG parse error" let (dpix, dpiy) = findpHYs rest'' - return $ ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy } + return ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy } findpHYs :: ByteString -> (Integer, Integer) -findpHYs x = - if B.null x || "IDAT" `B.isPrefixOf` x - then (72,72) -- default, no pHYs - else if "pHYs" `B.isPrefixOf` x - then let [x1,x2,x3,x4,y1,y2,y3,y4,u] = map fromIntegral - $ unpack $ B.take 9 $ B.drop 4 x - factor = if u == 1 -- dots per meter - then \z -> z * 254 `div` 10000 - else const 72 - in ( factor $ (shift x1 24) + (shift x2 16) + (shift x3 8) + x4, - factor $ (shift y1 24) + (shift y2 16) + (shift y3 8) + y4 ) - else findpHYs $ B.drop 1 x -- read another byte +findpHYs x + | B.null x || "IDAT" `B.isPrefixOf` x = (72,72) + | "pHYs" `B.isPrefixOf` x = + let [x1,x2,x3,x4,y1,y2,y3,y4,u] = + map fromIntegral $ unpack $ B.take 9 $ B.drop 4 x + factor = if u == 1 -- dots per meter + then \z -> z * 254 `div` 10000 + else const 72 + in ( factor $ (shift x1 24) + (shift x2 16) + (shift x3 8) + x4, + factor $ (shift y1 24) + (shift y2 16) + (shift y3 8) + y4 ) + | otherwise = findpHYs $ B.drop 1 x -- read another byte gifSize :: ByteString -> Maybe ImageSize gifSize img = do @@ -327,16 +342,16 @@ jpegSize img = jfifSize :: ByteString -> Either String ImageSize jfifSize rest = let [dpiDensity,dpix1,dpix2,dpiy1,dpiy2] = map fromIntegral - $ unpack $ B.take 5 $ B.drop 9 $ rest + $ unpack $ B.take 5 $B.drop 9 rest factor = case dpiDensity of 1 -> id - 2 -> \x -> (x * 254 `div` 10) + 2 -> \x -> x * 254 `div` 10 _ -> const 72 dpix = factor (shift dpix1 8 + dpix2) dpiy = factor (shift dpiy1 8 + dpiy2) in case findJfifSize rest of Left msg -> Left msg - Right (w,h) -> Right $ ImageSize { pxX = w + Right (w,h) ->Right ImageSize { pxX = w , pxY = h , dpiX = dpix , dpiY = dpiy } @@ -370,7 +385,7 @@ runGet' p bl = exifSize :: ByteString -> Either String ImageSize -exifSize bs = runGet' header $ bl +exifSize bs =runGet' header bl where bl = BL.fromChunks [bs] header = runExceptT $ exifHeader bl -- NOTE: It would be nicer to do @@ -440,14 +455,13 @@ exifHeader hdr = do Left msg -> throwError msg Right x -> return x return (tag, payload) - entries <- sequence $ replicate (fromIntegral numentries) ifdEntry + entries <- replicateM (fromIntegral numentries) ifdEntry subentries <- case lookup ExifOffset entries of Just (UnsignedLong offset') -> do pos <- lift bytesRead lift $ skip (fromIntegral offset' - (fromIntegral pos - 8)) numsubentries <- lift getWord16 - sequence $ - replicate (fromIntegral numsubentries) ifdEntry + replicateM (fromIntegral numsubentries) ifdEntry _ -> return [] let allentries = entries ++ subentries (wdth, hght) <- case (lookup ExifImageWidth allentries, @@ -458,13 +472,13 @@ exifHeader hdr = do -- we return a default width and height when -- the exif header doesn't contain these let resfactor = case lookup ResolutionUnit allentries of - Just (UnsignedShort 1) -> (100 / 254) + Just (UnsignedShort 1) -> 100 / 254 _ -> 1 let xres = maybe 72 (\(UnsignedRational x) -> floor $ x * resfactor) $ lookup XResolution allentries let yres = maybe 72 (\(UnsignedRational x) -> floor $ x * resfactor) $ lookup YResolution allentries - return $ ImageSize{ + return ImageSize{ pxX = wdth , pxY = hght , dpiX = xres @@ -588,3 +602,4 @@ tagTypeTable = M.fromList , (0xa300, FileSource) , (0xa301, SceneType) ] + diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 59b010034..da8c775f6 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Logging - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -39,6 +39,7 @@ module Text.Pandoc.Logging ( , messageVerbosity ) where +import Control.Monad (mzero) import Data.Aeson import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) @@ -56,12 +57,22 @@ data Verbosity = ERROR | WARNING | INFO | DEBUG instance ToJSON Verbosity where toJSON x = toJSON (show x) +instance FromJSON Verbosity where + parseJSON (String t) = + case t of + "ERROR" -> return ERROR + "WARNING" -> return WARNING + "INFO" -> return INFO + "DEBUG" -> return DEBUG + _ -> mzero + parseJSON _ = mzero data LogMessage = SkippedContent String SourcePos | CouldNotParseYamlMetadata String SourcePos | DuplicateLinkReference String SourcePos | DuplicateNoteReference String SourcePos + | NoteDefinedButNotUsed String SourcePos | DuplicateIdentifier String SourcePos | ReferenceNotFound String SourcePos | CircularReference String SourcePos @@ -78,6 +89,7 @@ data LogMessage = | CouldNotConvertTeXMath String String | CouldNotParseCSS String | Fetching String + | Extracting String | NoTitleElement String | NoLangSpecified | CouldNotHighlight String @@ -103,6 +115,11 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] + NoteDefinedButNotUsed s pos -> + ["key" .= Text.pack s, + "source" .= Text.pack (sourceName pos), + "line" .= toJSON (sourceLine pos), + "column" .= toJSON (sourceColumn pos)] DuplicateNoteReference s pos -> ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), @@ -162,6 +179,8 @@ instance ToJSON LogMessage where ["message" .= Text.pack msg] Fetching fp -> ["path" .= Text.pack fp] + Extracting fp -> + ["path" .= Text.pack fp] NoTitleElement fallback -> ["fallback" .= Text.pack fallback] NoLangSpecified -> [] @@ -193,6 +212,9 @@ showLogMessage msg = "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos DuplicateNoteReference s pos -> "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos + NoteDefinedButNotUsed s pos -> + "Note with key '" ++ s ++ "' defined at " ++ showPos pos ++ + " but not used." DuplicateIdentifier s pos -> "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos ReferenceNotFound s pos -> @@ -229,6 +251,8 @@ showLogMessage msg = "Could not parse CSS" ++ if null m then "" else (':':'\n':m) Fetching fp -> "Fetching " ++ fp ++ "..." + Extracting fp -> + "Extracting " ++ fp ++ "..." NoTitleElement fallback -> "This document format requires a nonempty <title> element.\n" ++ "Please specify either 'title' or 'pagetitle' in the metadata.\n" ++ @@ -242,10 +266,11 @@ showLogMessage msg = messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = case msg of - SkippedContent{} -> INFO + SkippedContent{} -> WARNING CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING + NoteDefinedButNotUsed{} -> WARNING DuplicateIdentifier{} -> WARNING ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING @@ -262,6 +287,7 @@ messageVerbosity msg = CouldNotConvertTeXMath{} -> WARNING CouldNotParseCSS{} -> WARNING Fetching{} -> INFO + Extracting{} -> INFO NoTitleElement{} -> WARNING NoLangSpecified -> INFO CouldNotHighlight{} -> WARNING diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f4a22b92a..f74c0e425 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,8 +15,8 @@ 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 -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {- | Module : Text.Pandoc.Lua @@ -30,12 +30,12 @@ Pandoc lua utils. -} module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where -import Control.Monad ( (>=>), when ) -import Control.Monad.Trans ( MonadIO(..) ) -import Data.Map ( Map ) -import Scripting.Lua ( LuaState, StackValue(..) ) +import Control.Monad (unless, when, (>=>)) +import Control.Monad.Trans (MonadIO (..)) +import Data.Map (Map) +import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) +import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk @@ -80,7 +80,7 @@ pushGlobalFilter lua = *> Lua.rawseti lua (-2) 1 runAll :: [LuaFilter] -> Pandoc -> IO Pandoc -runAll [] = return +runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc @@ -225,7 +225,7 @@ instance StackValue LuaFilterFunction where push lua v = pushFilterFunction lua v peek lua i = do isFn <- Lua.isfunction lua i - when (not isFn) (error $ "Not a function at index " ++ (show i)) + unless isFn (error $ "Not a function at index " ++ (show i)) Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex diff --git a/src/Text/Pandoc/Lua/Compat.hs b/src/Text/Pandoc/Lua/Compat.hs index 998d8d032..3fc81a15c 100644 --- a/src/Text/Pandoc/Lua/Compat.hs +++ b/src/Text/Pandoc/Lua/Compat.hs @@ -28,13 +28,13 @@ Compatibility helpers for hslua -} module Text.Pandoc.Lua.Compat ( loadstring ) where -import Scripting.Lua ( LuaState ) +import Scripting.Lua (LuaState) import qualified Scripting.Lua as Lua -- | Interpret string as lua code and load into the lua environment. loadstring :: LuaState -> String -> String -> IO Int #if MIN_VERSION_hslua(0,5,0) -loadstring lua script _ = Lua.loadstring lua script +loadstring lua script _ = Lua.loadstring lua script #else loadstring lua script cn = Lua.loadstring lua script cn #endif diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 15f19f024..27c19d4f0 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -27,25 +27,24 @@ Pandoc module for lua. -} module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where -import Data.ByteString.Char8 ( unpack ) -import Data.Default ( Default(..) ) -import Scripting.Lua ( LuaState, call, push, pushhsfunction, rawset) -import Text.Pandoc.Class hiding ( readDataFile ) -import Text.Pandoc.Definition ( Pandoc ) -import Text.Pandoc.Lua.Compat ( loadstring ) +import Control.Monad (unless) +import Data.ByteString.Char8 (unpack) +import Data.Default (Default (..)) +import Data.Text (pack) +import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) +import Text.Pandoc.Class hiding (readDataFile) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Lua.Compat (loadstring) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Readers ( Reader(..), getReader ) -import Text.Pandoc.Shared ( readDataFile ) +import Text.Pandoc.Readers (Reader (..), getReader) +import Text.Pandoc.Shared (readDataFile) -- | Push the "pandoc" on the lua stack. pushPandocModule :: LuaState -> IO () pushPandocModule lua = do script <- pandocModuleScript status <- loadstring lua script "pandoc.lua" - if (status /= 0) - then return () - else do - call lua 0 1 + unless (status /= 0) $ call lua 0 1 push lua "__read" pushhsfunction lua read_doc rawset lua (-3) @@ -57,13 +56,13 @@ pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" read_doc :: String -> String -> IO (Either String Pandoc) read_doc formatSpec content = do case getReader formatSpec of - Left s -> return $ Left s + Left s -> return $ Left s Right reader -> case reader of - StringReader r -> do - res <- runIO $ r def content + TextReader r -> do + res <- runIO $ r def (pack content) case res of - Left s -> return . Left $ show s + Left s -> return . Left $ show s Right pd -> return $ Right pd _ -> return $ Left "Only string formats are supported at the moment." diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs index 3d2d29ebf..a5d4ba1e9 100644 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify @@ -16,9 +16,9 @@ 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 -} -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} #if !MIN_VERSION_base(4,8,0) {-# LANGUAGE OverlappingInstances #-} #endif @@ -36,8 +36,8 @@ Shared StackValue instances for pandoc and generic types. -} module Text.Pandoc.Lua.SharedInstances () where -import Scripting.Lua ( LTYPE(..), StackValue(..), newtable ) -import Text.Pandoc.Lua.Util ( addRawInt, addValue, getRawInt, keyValuePairs ) +import Scripting.Lua (LTYPE (..), StackValue (..), newtable) +import Text.Pandoc.Lua.Util (addRawInt, addValue, getRawInt, keyValuePairs) import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 @@ -112,5 +112,5 @@ instance (StackValue a, StackValue b) => StackValue (Either a b) where peek lua idx = peek lua idx >>= \case Just left -> return . Just $ Left left Nothing -> fmap Right <$> peek lua idx - valuetype (Left x) = valuetype x + valuetype (Left x) = valuetype x valuetype (Right x) = valuetype x diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 03f6e06e2..d2e3f630a 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2015 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify @@ -17,11 +17,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances - Copyright : © 2012-2016 John MacFarlane + Copyright : © 2012-2017 John MacFarlane © 2017 Albert Krewinkel License : GNU GPL, version 2 or above @@ -32,13 +32,13 @@ StackValue instances for pandoc types. -} module Text.Pandoc.Lua.StackInstances () where -import Control.Applicative ( (<|>) ) -import Scripting.Lua - ( LTYPE(..), LuaState, StackValue(..), ltype, newtable, objlen ) +import Control.Applicative ((<|>)) +import Scripting.Lua (LTYPE (..), LuaState, StackValue (..), ltype, newtable, + objlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.SharedInstances () -import Text.Pandoc.Lua.Util ( addValue, getTable, pushViaConstructor ) -import Text.Pandoc.Shared ( safeRead ) +import Text.Pandoc.Lua.Util (addValue, getTable, pushViaConstructor) +import Text.Pandoc.Shared (safeRead) instance StackValue Pandoc where push lua (Pandoc meta blocks) = do diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index f0b87c231..0a704d027 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify @@ -42,10 +42,8 @@ module Text.Pandoc.Lua.Util , pushViaConstructor ) where -import Scripting.Lua - ( LuaState, StackValue(..) - , call, getglobal2, gettable, next, pop, pushnil, rawgeti, rawseti, settable - ) +import Scripting.Lua (LuaState, StackValue (..), call, getglobal2, gettable, + next, pop, pushnil, rawgeti, rawseti, settable) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index 2e4a97b71..162112634 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MIME - Copyright : Copyright (C) 2011-2016 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index b865f97c2..d8d6da345 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2014 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MediaBag - Copyright : Copyright (C) 2014 John MacFarlane + Copyright : Copyright (C) 2014-2015, 2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,21 +35,15 @@ module Text.Pandoc.MediaBag ( lookupMedia, insertMedia, mediaDirectory, - extractMediaBag ) where -import Control.Monad (when) -import Control.Monad.Trans (MonadIO (..)) import qualified Data.ByteString.Lazy as BL import Data.Data (Data) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Typeable (Typeable) -import System.Directory (createDirectoryIfMissing) import System.FilePath import qualified System.FilePath.Posix as Posix -import System.IO (stderr) import Text.Pandoc.MIME (MimeType, getMimeTypeDef) -import qualified Text.Pandoc.UTF8 as UTF8 -- | A container for a collection of binary resources, with names and -- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty' @@ -87,28 +81,3 @@ mediaDirectory :: MediaBag -> [(String, MimeType, Int)] mediaDirectory (MediaBag mediamap) = M.foldWithKey (\fp (mime,contents) -> (((Posix.joinPath fp), mime, fromIntegral $ BL.length contents):)) [] mediamap - --- | Extract contents of MediaBag to a given directory. Print informational --- messages if 'verbose' is true. --- TODO: eventually we may want to put this into PandocMonad --- In PandocPure, it could write to the fake file system... -extractMediaBag :: MonadIO m - => Bool - -> FilePath - -> MediaBag - -> m () -extractMediaBag verbose dir (MediaBag mediamap) = liftIO $ do - sequence_ $ M.foldWithKey - (\fp (_ ,contents) -> - ((writeMedia verbose dir (Posix.joinPath fp, contents)):)) [] mediamap - -writeMedia :: Bool -> FilePath -> (FilePath, BL.ByteString) -> IO () -writeMedia verbose dir (subpath, bs) = do - -- we join and split to convert a/b/c to a\b\c on Windows; - -- in zip containers all paths use / - let fullpath = dir </> normalise subpath - createDirectoryIfMissing True $ takeDirectory fullpath - when verbose $ UTF8.hPutStrLn stderr $ "pandoc: extracting " ++ fullpath - BL.writeFile fullpath bs - - diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 0b09f0497..c7211c86e 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Options - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -45,6 +45,8 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , def , isEnabled ) where +import Data.Aeson (ToJSON(..), FromJSON(..), + genericToEncoding, defaultOptions) import Data.Data (Data) import Data.Default import qualified Data.Set as Set @@ -104,17 +106,29 @@ data HTMLMathMethod = PlainMath | KaTeX String String -- url of stylesheet and katex.js deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON HTMLMathMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON HTMLMathMethod + data CiteMethod = Citeproc -- use citeproc to render them | Natbib -- output natbib cite commands | Biblatex -- output biblatex cite commands deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON CiteMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON CiteMethod + -- | Methods for obfuscating email addresses in HTML. data ObfuscationMethod = NoObfuscation | ReferenceObfuscation | JavascriptObfuscation deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON ObfuscationMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ObfuscationMethod + -- | Varieties of HTML slide shows. data HTMLSlideVariant = S5Slides | SlidySlides @@ -124,18 +138,30 @@ data HTMLSlideVariant = S5Slides | NoSlides deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON HTMLSlideVariant where + toEncoding = genericToEncoding defaultOptions +instance FromJSON HTMLSlideVariant + -- | Options for accepting or rejecting MS Word track-changes. data TrackChanges = AcceptChanges | RejectChanges | AllChanges deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON TrackChanges where + toEncoding = genericToEncoding defaultOptions +instance FromJSON TrackChanges + -- | Options for wrapping text in the output. data WrapOption = WrapAuto -- ^ Automatically wrap to width | WrapNone -- ^ No non-semantic newlines | WrapPreserve -- ^ Preserve wrapping of input source deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON WrapOption where + toEncoding = genericToEncoding defaultOptions +instance FromJSON WrapOption + -- | Options defining the type of top-level headers. data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts | TopLevelChapter -- ^ Top-level headers become chapters @@ -144,12 +170,20 @@ data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts -- heuristics deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON TopLevelDivision where + toEncoding = genericToEncoding defaultOptions +instance FromJSON TopLevelDivision + -- | Locations for footnotes and references in markdown output data ReferenceLocation = EndOfBlock -- ^ End of block | EndOfSection -- ^ prior to next section header (or end of document) | EndOfDocument -- ^ at end of document deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON ReferenceLocation where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ReferenceLocation + -- | Options for writers data WriterOptions = WriterOptions { writerTemplate :: Maybe String -- ^ Template to use diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 696dbacf0..cd75d869d 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.PDF - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,15 +34,15 @@ module Text.Pandoc.PDF ( makePDF ) where import qualified Codec.Picture as JP import qualified Control.Exception as E -import Control.Monad (unless, when, (<=<)) +import Control.Monad (unless, when) import Control.Monad.Trans (MonadIO (..)) +import qualified Data.Text as T +import Data.Text (Text) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BC -import Data.Digest.Pure.SHA (sha1, showDigest) -import Data.List (isInfixOf) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import System.Directory @@ -53,7 +53,7 @@ import System.IO (stdout) import System.IO.Temp (withTempDirectory, withTempFile) import Text.Pandoc.Definition import Text.Pandoc.MediaBag -import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) +import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Shared (inDirectory, stringify, withTempDir) @@ -63,8 +63,9 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) #ifdef _WINDOWS import Data.List (intercalate) #endif -import Text.Pandoc.Class (PandocIO, fetchItem, report, runIO, runIOorExplode, - setMediaBag, setVerbosity) +import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, + setMediaBag, setVerbosity, getResourcePath, + setResourcePath, fillMediaBag, extractMedia) import Text.Pandoc.Logging #ifdef _WINDOWS @@ -72,16 +73,15 @@ changePathSeparators :: FilePath -> FilePath changePathSeparators = intercalate "/" . splitDirectories #endif -makePDF :: MonadIO m - => String -- ^ pdf creator (pdflatex, lualatex, +makePDF :: String -- ^ pdf creator (pdflatex, lualatex, -- xelatex, context, wkhtmltopdf, pdfroff) - -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer + -> (WriterOptions -> Pandoc -> PandocIO Text) -- ^ writer -> WriterOptions -- ^ options -> Verbosity -- ^ verbosity level -> MediaBag -- ^ media -> Pandoc -- ^ document - -> m (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do + -> PandocIO (Either ByteString ByteString) +makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = do let mathArgs = case writerHTMLMathMethod opts of -- with MathJax, wait til all math is rendered: MathJax _ -> ["--run-script", "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });", @@ -102,23 +102,20 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do ,("margin-left", fromMaybe (Just "1.25in") (getField "margin-left" meta')) ] - source <- runIOorExplode $ do - setVerbosity verbosity - writer opts doc - html2pdf verbosity args source -makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do - source <- runIOorExplode $ do - setVerbosity verbosity - writer opts doc + source <- writer opts doc + liftIO $ html2pdf verbosity args source +makePDF "pdfroff" writer opts verbosity _mediabag doc = do + source <- writer opts doc let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", "--no-toc-relocation"] - ms2pdf verbosity args source + liftIO $ ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." else withTempDir + resourcePath <- getResourcePath liftIO $ withTemp "tex2pdf." $ \tmpdir -> do - doc' <- handleImages verbosity opts mediabag tmpdir doc + doc' <- handleImages verbosity opts resourcePath mediabag tmpdir doc source <- runIOorExplode $ do setVerbosity verbosity writer opts doc' @@ -131,44 +128,19 @@ makePDF program writer opts verbosity mediabag doc = do handleImages :: Verbosity -> WriterOptions + -> [FilePath] -> MediaBag -> FilePath -- ^ temp dir to store images -> Pandoc -- ^ document -> IO Pandoc -handleImages verbosity opts mediabag tmpdir = - walkM (convertImages verbosity tmpdir) <=< - walkM (handleImage' verbosity opts mediabag tmpdir) - -handleImage' :: Verbosity - -> WriterOptions - -> MediaBag - -> FilePath - -> Inline - -> IO Inline -handleImage' verbosity opts mediabag tmpdir (Image attr ils (src,tit)) = do - exists <- doesFileExist src - if exists - then return $ Image attr ils (src,tit) - else do - res <- runIO $ do - setVerbosity verbosity - setMediaBag mediabag - fetchItem (writerSourceURL opts) src - case res of - Right (contents, Just mime) -> do - let ext = fromMaybe (takeExtension src) $ - extensionFromMimeType mime - let basename = showDigest $ sha1 $ BL.fromChunks [contents] - let fname = tmpdir </> basename <.> ext - BS.writeFile fname contents - return $ Image attr ils (fname,tit) - _ -> do - runIO $ do - setVerbosity verbosity - report $ CouldNotFetchResource src "skipping..." - -- return alt text - return $ Emph ils -handleImage' _ _ _ _ x = return x +handleImages verbosity opts resourcePath mediabag tmpdir doc = do + doc' <- runIOorExplode $ do + setVerbosity verbosity + setResourcePath resourcePath + setMediaBag mediabag + fillMediaBag (writerSourceURL opts) doc >>= + extractMedia tmpdir + walkM (convertImages verbosity tmpdir) doc' convertImages :: Verbosity -> FilePath -> Inline -> IO Inline convertImages verbosity tmpdir (Image attr ils (src, tit)) = do @@ -191,6 +163,7 @@ convertImage tmpdir fname = Just "image/png" -> doNothing Just "image/jpeg" -> doNothing Just "application/pdf" -> doNothing + Just "image/svg+xml" -> return $ Left "conversion from svg not supported" _ -> JP.readImage fname >>= \res -> case res of Left e -> return $ Left e @@ -206,10 +179,10 @@ tex2pdf' :: Verbosity -- ^ Verbosity level -> [String] -- ^ Arguments to the latex-engine -> FilePath -- ^ temp directory for output -> String -- ^ tex program - -> String -- ^ tex source + -> Text -- ^ tex source -> IO (Either ByteString ByteString) tex2pdf' verbosity args tmpDir program source = do - let numruns = if "\\tableofcontents" `isInfixOf` source + let numruns = if "\\tableofcontents" `T.isInfixOf` source then 3 -- to get page numbers else 2 -- 1 run won't give you PDF bookmarks (exit, log', mbPdf) <- runTeXProgram verbosity program args 1 numruns tmpDir source @@ -251,11 +224,11 @@ extractConTeXtMsg log' = do -- contents of stdout, contents of produced PDF if any). Rerun -- a fixed number of times to resolve references. runTeXProgram :: Verbosity -> String -> [String] -> Int -> Int -> FilePath - -> String -> IO (ExitCode, ByteString, Maybe ByteString) + -> Text -> IO (ExitCode, ByteString, Maybe ByteString) runTeXProgram verbosity program args runNumber numRuns tmpDir source = do let file = tmpDir </> "input.tex" exists <- doesFileExist file - unless exists $ UTF8.writeFile file source + unless exists $ BS.writeFile file $ UTF8.fromText source #ifdef _WINDOWS -- note: we want / even on Windows, for TexLive let tmpDir' = changePathSeparators tmpDir @@ -304,7 +277,7 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do ms2pdf :: Verbosity -> [String] - -> String + -> Text -> IO (Either ByteString ByteString) ms2pdf verbosity args source = do env' <- getEnvironment @@ -316,10 +289,10 @@ ms2pdf verbosity args source = do mapM_ print env' putStr "\n" putStrLn $ "[makePDF] Contents:\n" - putStr source + putStr $ T.unpack source putStr "\n" (exit, out) <- pipeProcess (Just env') "pdfroff" args - (UTF8.fromStringLazy source) + (BL.fromStrict $ UTF8.fromText source) when (verbosity >= INFO) $ do B.hPutStr stdout out putStr "\n" @@ -329,12 +302,12 @@ ms2pdf verbosity args source = do html2pdf :: Verbosity -- ^ Verbosity level -> [String] -- ^ Args to wkhtmltopdf - -> String -- ^ HTML5 source + -> Text -- ^ HTML5 source -> IO (Either ByteString ByteString) html2pdf verbosity args source = do file <- withTempFile "." "html2pdf.html" $ \fp _ -> return fp pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp - UTF8.writeFile file source + BS.writeFile file $ UTF8.fromText source let programArgs = args ++ [file, pdfFile] env' <- getEnvironment when (verbosity >= INFO) $ do @@ -369,11 +342,11 @@ html2pdf verbosity args source = do context2pdf :: Verbosity -- ^ Verbosity level -> FilePath -- ^ temp directory for output - -> String -- ^ ConTeXt source + -> Text -- ^ ConTeXt source -> IO (Either ByteString ByteString) context2pdf verbosity tmpDir source = inDirectory tmpDir $ do let file = "input.tex" - UTF8.writeFile file source + BS.writeFile file $ UTF8.fromText source #ifdef _WINDOWS -- note: we want / even on Windows, for TexLive let tmpDir' = changePathSeparators tmpDir diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a6d3cd46a..cd51bff69 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -7,7 +7,7 @@ , IncoherentInstances #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Parsing - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -36,6 +36,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA A utility library with parsers used in pandoc readers. -} module Text.Pandoc.Parsing ( anyLine, + anyLineNewline, + indentWith, many1Till, notFollowedBy', oneOfStrings, @@ -48,6 +50,7 @@ module Text.Pandoc.Parsing ( anyLine, enclosed, stringAnyCase, parseFromString, + parseFromString', lineClump, charsInBalanced, romanNumeral, @@ -66,6 +69,7 @@ module Text.Pandoc.Parsing ( anyLine, tableWith, widthsFromIndices, gridTableWith, + gridTableWith', readWith, readWithM, testStringWith, @@ -82,6 +86,7 @@ module Text.Pandoc.Parsing ( anyLine, HasMacros (..), HasLogMessages (..), HasLastStrPosition (..), + HasIncludeFiles (..), defaultParserState, HeaderType (..), ParserContext (..), @@ -119,6 +124,7 @@ module Text.Pandoc.Parsing ( anyLine, (<+?>), extractIdClass, insertIncludedFile, + insertIncludedFileF, -- * Re-exports from Text.Pandoc.Parsec Stream, runParser, @@ -252,12 +258,28 @@ anyLine = do return this _ -> mzero +-- | Parse any line, include the final newline in the output +anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLineNewline = (++ "\n") <$> anyLine + +-- | Parse indent by specified number of spaces (or equiv. tabs) +indentWith :: Stream [Char] m Char + => HasReaderOptions st + => Int -> ParserT [Char] st m [Char] +indentWith num = do + tabStop <- getOption readerTabStop + if (num < tabStop) + then count num (char ' ') + else choice [ try (count num (char ' ')) + , try (char '\t' >> indentWith (num - tabStop)) ] + -- | Like @manyTill@, but reads at least one item. -many1Till :: Stream s m t +many1Till :: (Show end, Stream s m t) => ParserT s st m a -> ParserT s st m end -> ParserT s st m [a] many1Till p end = do + notFollowedBy' end first <- p rest <- manyTill p end return (first:rest) @@ -322,7 +344,7 @@ blanklines :: Stream s m Char => ParserT s st m [Char] blanklines = many1 blankline -- | Parses material enclosed between start and end parsers. -enclosed :: Stream s m Char => ParserT s st m t -- ^ start parser +enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser -> ParserT s st m end -- ^ end parser -> ParserT s st m a -- ^ content parser (to be used repeatedly) -> ParserT s st m [a] @@ -338,7 +360,10 @@ stringAnyCase (x:xs) = do return (firstChar:rest) -- | Parse contents of 'str' using 'parser' and return result. -parseFromString :: Monad m => ParserT String st m a -> String -> ParserT String st m a +parseFromString :: Monad m + => ParserT String st m a + -> String + -> ParserT String st m a parseFromString parser str = do oldPos <- getPosition oldInput <- getInput @@ -350,6 +375,18 @@ parseFromString parser str = do setPosition oldPos return result +-- | Like 'parseFromString' but specialized for 'ParserState'. +-- This resets 'stateLastStrPos', which is almost always what we want. +parseFromString' :: Monad m + => ParserT String ParserState m a + -> String + -> ParserT String ParserState m a +parseFromString' parser str = do + oldStrPos <- stateLastStrPos <$> getState + res <- parseFromString parser str + updateState $ \st -> st{ stateLastStrPos = oldStrPos } + return res + -- | Parse raw line block up to and including blank lines. lineClump :: Stream [Char] m Char => ParserT [Char] st m String lineClump = blanklines @@ -445,33 +482,8 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) sepby1 p sep = (:) <$> p <*> (many (try $ sep >> p)) --- Schemes from http://www.iana.org/assignments/uri-schemes.html plus --- the unofficial schemes coap, doi, javascript, isbn, pmid -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", "isbn", "pmid"] - uriScheme :: Stream s m Char => ParserT s st m String -uriScheme = oneOfStringsCI schemes +uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) @@ -762,21 +774,36 @@ lineBlockLines = try $ do -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. -tableWith :: Stream s m Char - => ParserT s ParserState m ([Blocks], [Alignment], [Int]) - -> ([Int] -> ParserT s ParserState m [Blocks]) - -> ParserT s ParserState m sep - -> ParserT s ParserState m end - -> ParserT s ParserState m Blocks +tableWith :: (Stream s m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT s st m (mf [Blocks], [Alignment], [Int]) + -> ([Int] -> ParserT s st m (mf [Blocks])) + -> ParserT s st m sep + -> ParserT s st m end + -> ParserT s st m (mf Blocks) tableWith headerParser rowParser lineParser footerParser = try $ do + (aligns, widths, heads, rows) <- tableWith' headerParser rowParser + lineParser footerParser + return $ B.table mempty (zip aligns widths) <$> heads <*> rows + +type TableComponents mf = ([Alignment], [Double], mf [Blocks], mf [[Blocks]]) + +tableWith' :: (Stream s m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT s st m (mf [Blocks], [Alignment], [Int]) + -> ([Int] -> ParserT s st m (mf [Blocks])) + -> ParserT s st m sep + -> ParserT s st m end + -> ParserT s st m (TableComponents mf) +tableWith' headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser - lines' <- rowParser indices `sepEndBy1` lineParser + lines' <- sequence <$> rowParser indices `sepEndBy1` lineParser footerParser numColumns <- getOption readerColumns let widths = if (indices == []) then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ B.table mempty (zip aligns widths) heads lines' + return $ (aligns, widths, heads, lines') -- Calculate relative widths of table columns, based on indices widthsFromIndices :: Int -- Number of columns on terminal @@ -809,25 +836,44 @@ widthsFromIndices numColumns' indices = -- (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 :: Stream [Char] m Char - => ParserT [Char] ParserState m Blocks -- ^ Block list parser - -> Bool -- ^ Headerless table - -> ParserT [Char] ParserState m Blocks +gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -- ^ Block list parser + -> Bool -- ^ Headerless table + -> ParserT [Char] st m (mf Blocks) gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter +gridTableWith' :: (Stream [Char] m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -- ^ Block list parser + -> Bool -- ^ Headerless table + -> ParserT [Char] st m (TableComponents mf) +gridTableWith' blocks headless = + tableWith' (gridTableHeader headless blocks) (gridTableRow blocks) + (gridTableSep '-') gridTableFooter + gridTableSplitLine :: [Int] -> String -> [String] gridTableSplitLine indices line = map removeFinalBar $ tail $ splitStringByIndices (init indices) $ trimr line -gridPart :: Stream s m Char => Char -> ParserT s st m (Int, Int) +gridPart :: Stream s m Char => Char -> ParserT s st m ((Int, Int), Alignment) gridPart ch = do + leftColon <- option False (True <$ char ':') dashes <- many1 (char ch) + rightColon <- option False (True <$ char ':') char '+' - return (length dashes, length dashes + 1) - -gridDashedLines :: Stream s m Char => Char -> ParserT s st m [(Int,Int)] + let lengthDashes = length dashes + (if leftColon then 1 else 0) + + (if rightColon then 1 else 0) + let alignment = case (leftColon, rightColon) of + (True, True) -> AlignCenter + (True, False) -> AlignLeft + (False, True) -> AlignRight + (False, False) -> AlignDefault + return ((lengthDashes, lengthDashes + 1), alignment) + +gridDashedLines :: Stream s m Char => Char -> ParserT s st m [((Int, Int), Alignment)] gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline removeFinalBar :: String -> String @@ -835,14 +881,14 @@ removeFinalBar = reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse -- | Separator between rows of grid table. -gridTableSep :: Stream s m Char => Char -> ParserT s ParserState m Char +gridTableSep :: Stream s m Char => Char -> ParserT s st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: Stream [Char] m Char +gridTableHeader :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) => Bool -- ^ Headerless table - -> ParserT [Char] ParserState m Blocks - -> ParserT [Char] ParserState m ([Blocks], [Alignment], [Int]) + -> ParserT [Char] st m (mf Blocks) + -> ParserT [Char] st m (mf [Blocks], [Alignment], [Int]) gridTableHeader headless blocks = try $ do optional blanklines dashes <- gridDashedLines '-' @@ -851,36 +897,40 @@ gridTableHeader headless blocks = try $ do else many1 (notFollowedBy (gridTableSep '=') >> char '|' >> many1Till anyChar newline) - if headless - then return () - else gridTableSep '=' >> return () - let lines' = map snd dashes + underDashes <- if headless + then return dashes + else gridDashedLines '=' + guard $ length dashes == length underDashes + let lines' = map (snd . fst) underDashes let indices = scanl (+) 0 lines' - let aligns = replicate (length lines') AlignDefault - -- RST does not have a notion of alignments + let aligns = map snd underDashes let rawHeads = if headless - then replicate (length dashes) "" - else map (intercalate " ") $ transpose + then replicate (length underDashes) "" + else map (unlines . map trim) $ transpose $ map (gridTableSplitLine indices) rawContent - heads <- mapM (parseFromString blocks) $ map trim rawHeads + heads <- fmap sequence $ mapM (parseFromString blocks . trim) rawHeads return (heads, aligns, indices) -gridTableRawLine :: Stream s m Char => [Int] -> ParserT s ParserState m [String] +gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [String] gridTableRawLine indices = do char '|' line <- many1Till anyChar newline return (gridTableSplitLine indices line) -- | Parse row of grid table. -gridTableRow :: Stream [Char] m Char - => ParserT [Char] ParserState m Blocks +gridTableRow :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -> [Int] - -> ParserT [Char] ParserState m [Blocks] + -> ParserT [Char] st m (mf [Blocks]) gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ transpose colLines - mapM (liftM compactifyCell . parseFromString blocks) cols + compactifyCell bs = case compactify [bs] of + [] -> mempty + x:_ -> x + cells <- sequence <$> mapM (parseFromString blocks) cols + return $ fmap (map compactifyCell) cells removeOneLeadingSpace :: [String] -> [String] removeOneLeadingSpace xs = @@ -890,11 +940,8 @@ removeOneLeadingSpace xs = where startsWithSpace "" = True startsWithSpace (y:_) = y == ' ' -compactifyCell :: Blocks -> Blocks -compactifyCell bs = head $ compactify [bs] - -- | Parse footer for a grid table. -gridTableFooter :: Stream s m Char => ParserT s ParserState m [Char] +gridTableFooter :: Stream s m Char => ParserT s st m [Char] gridTableFooter = blanklines --- @@ -937,6 +984,7 @@ data ParserState = ParserState stateSubstitutions :: SubstTable, -- ^ List of substitution references stateNotes :: NoteTable, -- ^ List of notes (raw bodies) stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies) + stateNoteRefs :: Set.Set String, -- ^ List of note references used stateMeta :: Meta, -- ^ Document metadata stateMeta' :: F Meta, -- ^ Document metadata stateCitations :: M.Map String String, -- ^ RST-style citations @@ -972,6 +1020,9 @@ class HasReaderOptions st where -- default getOption f = (f . extractReaderOptions) <$> getState +instance HasReaderOptions ParserState where + extractReaderOptions = stateOptions + class HasQuoteContext st m where getQuoteContext :: (Stream s m t) => ParsecT s st m QuoteContext withQuoteContext :: QuoteContext -> ParsecT s st m a -> ParsecT s st m a @@ -987,9 +1038,6 @@ instance Monad m => HasQuoteContext ParserState m where setState newState { stateQuoteContext = oldQuoteContext } return result -instance HasReaderOptions ParserState where - extractReaderOptions = stateOptions - class HasHeaderMap st where extractHeaderMap :: st -> M.Map Inlines String updateHeaderMap :: (M.Map Inlines String -> M.Map Inlines String) -> @@ -1031,6 +1079,16 @@ instance HasLogMessages ParserState where addLogMessage msg st = st{ stateLogMessages = msg : stateLogMessages st } getLogMessages st = reverse $ stateLogMessages st +class HasIncludeFiles st where + getIncludeFiles :: st -> [String] + addIncludeFile :: String -> st -> st + dropLatestIncludeFile :: st -> st + +instance HasIncludeFiles ParserState where + getIncludeFiles = stateContainers + addIncludeFile f s = s{ stateContainers = f : stateContainers s } + dropLatestIncludeFile s = s { stateContainers = drop 1 $ stateContainers s } + defaultParserState :: ParserState defaultParserState = ParserState { stateOptions = def, @@ -1043,7 +1101,8 @@ defaultParserState = stateHeaderKeys = M.empty, stateSubstitutions = M.empty, stateNotes = [], - stateNotes' = [], + stateNotes' = M.empty, + stateNoteRefs = Set.empty, stateMeta = nullMeta, stateMeta' = return nullMeta, stateCitations = M.empty, @@ -1110,7 +1169,8 @@ data QuoteContext type NoteTable = [(String, String)] -type NoteTable' = [(String, F Blocks)] -- used in markdown reader +type NoteTable' = M.Map String (SourcePos, F Blocks) +-- used in markdown reader newtype Key = Key String deriving (Show, Read, Eq, Ord) @@ -1322,17 +1382,18 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs') Nothing -> cls kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs -insertIncludedFile :: PandocMonad m - => ParserT String ParserState m Blocks - -> [FilePath] -> FilePath - -> ParserT String ParserState m Blocks -insertIncludedFile blocks dirs f = do +insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st, + Functor mf, Applicative mf, Monad mf) + => ParserT String st m (mf Blocks) + -> [FilePath] -> FilePath + -> ParserT String st m (mf Blocks) +insertIncludedFile' blocks dirs f = do oldPos <- getPosition oldInput <- getInput - containers <- stateContainers <$> getState + containers <- getIncludeFiles <$> getState when (f `elem` containers) $ throwError $ PandocParseError $ "Include file loop at " ++ show oldPos - updateState $ \s -> s{ stateContainers = f : stateContainers s } + updateState $ addIncludeFile f mbcontents <- readFileFromDirs dirs f contents <- case mbcontents of Just s -> return s @@ -1344,5 +1405,22 @@ insertIncludedFile blocks dirs f = do bs <- blocks setInput oldInput setPosition oldPos - updateState $ \s -> s{ stateContainers = tail $ stateContainers s } + updateState dropLatestIncludeFile return bs + +-- | Parse content of include file as blocks. Circular includes result in an +-- @PandocParseError@. +insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) + => ParserT String st m Blocks + -> [FilePath] -> FilePath + -> ParserT String st m Blocks +insertIncludedFile blocks dirs f = + runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f + +-- | Parse content of include file as future blocks. Circular includes result in +-- an @PandocParseError@. +insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st) + => ParserT String st m (Future st Blocks) + -> [FilePath] -> FilePath + -> ParserT String st m (Future st Blocks) +insertIncludedFileF = insertIncludedFile' diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 32e60843c..d78a2f1d9 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 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 @@ -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-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -108,10 +108,10 @@ data D = Text Int String | CarriageReturn | NewLine | BlankLines Int -- number of blank lines - deriving (Show) + deriving (Show, Eq) newtype Doc = Doc { unDoc :: Seq D } - deriving (Monoid, Show) + deriving (Monoid, Show, Eq) instance IsString Doc where fromString = text diff --git a/src/Text/Pandoc/Process.hs b/src/Text/Pandoc/Process.hs index 1014f37dd..b2a0c17f1 100644 --- a/src/Text/Pandoc/Process.hs +++ b/src/Text/Pandoc/Process.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Process - Copyright : Copyright (C) 2013-2016 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index e2d40336c..004fefe25 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -93,36 +93,37 @@ import Text.Pandoc.Shared (mapLeft) import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL +import Data.Text (Text) -data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) +data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) -- | Association list of formats and readers. readers :: PandocMonad m => [(String, Reader m)] -readers = [ ("native" , StringReader readNative) - ,("json" , StringReader $ \o s -> +readers = [ ("native" , TextReader readNative) + ,("json" , TextReader $ \o s -> case readJSON o s of Right doc -> return doc Left _ -> throwError $ PandocParseError "JSON parse error") - ,("markdown" , StringReader readMarkdown) - ,("markdown_strict" , StringReader readMarkdown) - ,("markdown_phpextra" , StringReader readMarkdown) - ,("markdown_github" , StringReader readMarkdown) - ,("markdown_mmd", StringReader readMarkdown) - ,("commonmark" , StringReader readCommonMark) - ,("rst" , StringReader readRST) - ,("mediawiki" , StringReader readMediaWiki) - ,("docbook" , StringReader readDocBook) - ,("opml" , StringReader readOPML) - ,("org" , StringReader readOrg) - ,("textile" , StringReader readTextile) -- TODO : textile+lhs - ,("html" , StringReader readHtml) - ,("latex" , StringReader readLaTeX) - ,("haddock" , StringReader readHaddock) - ,("twiki" , StringReader readTWiki) + ,("markdown" , TextReader readMarkdown) + ,("markdown_strict" , TextReader readMarkdown) + ,("markdown_phpextra" , TextReader readMarkdown) + ,("markdown_github" , TextReader readMarkdown) + ,("markdown_mmd", TextReader readMarkdown) + ,("commonmark" , TextReader readCommonMark) + ,("rst" , TextReader readRST) + ,("mediawiki" , TextReader readMediaWiki) + ,("docbook" , TextReader readDocBook) + ,("opml" , TextReader readOPML) + ,("org" , TextReader readOrg) + ,("textile" , TextReader readTextile) -- TODO : textile+lhs + ,("html" , TextReader readHtml) + ,("latex" , TextReader readLaTeX) + ,("haddock" , TextReader readHaddock) + ,("twiki" , TextReader readTWiki) ,("docx" , ByteStringReader readDocx) ,("odt" , ByteStringReader readOdt) - ,("t2t" , StringReader readTxt2Tags) + ,("t2t" , TextReader readTxt2Tags) ,("epub" , ByteStringReader readEPUB) ] @@ -134,7 +135,7 @@ getReader s = Right (readerName, setExts) -> case lookup readerName readers of Nothing -> Left $ "Unknown reader: " ++ readerName - Just (StringReader r) -> Right $ StringReader $ \o -> + Just (TextReader r) -> Right $ TextReader $ \o -> r o{ readerExtensions = setExts $ getDefaultExtensions readerName } Just (ByteStringReader r) -> Right $ ByteStringReader $ \o -> @@ -142,5 +143,6 @@ getReader s = getDefaultExtensions readerName } -- | Read pandoc document from JSON format. -readJSON :: ReaderOptions -> String -> Either PandocError Pandoc -readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy +readJSON :: ReaderOptions -> Text -> Either PandocError Pandoc +readJSON _ = + mapLeft PandocParseError . eitherDecode' . BL.fromStrict . UTF8.fromText diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index e98ee066e..3c62f8db5 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -34,15 +34,15 @@ where import CMark import Data.List (groupBy) -import Data.Text (pack, unpack) +import Data.Text (Text, unpack) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options -- | Parse a CommonMark formatted string into a 'Pandoc' structure. -readCommonMark :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ - nodeToPandoc $ commonmarkToNode opts' $ pack s + nodeToPandoc $ commonmarkToNode opts' s where opts' = if extensionEnabled Ext_smart (readerExtensions opts) then [optNormalize, optSmart] else [optNormalize] diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index bef256a93..bd3c7c356 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -16,6 +16,8 @@ import Text.TeXMath (readMathML, writeTeX) import Data.Default import Data.Foldable (asum) import Text.Pandoc.Class (PandocMonad) +import Data.Text (Text) +import qualified Data.Text as T {- @@ -522,11 +524,11 @@ instance Default DBState where , dbContent = [] } -readDocBook :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do - let tree = normalizeTree . parseXML . handleInstructions $ inp + let tree = normalizeTree . parseXML . handleInstructions $ T.unpack inp (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree - return $ Pandoc (dbMeta st') (toList . mconcat $ bs) + return $ Pandoc (dbMeta st') (toList . mconcat $ bs) -- We treat <?asciidoc-br?> specially (issue #1236), converting it -- to <br/>, since xml-light doesn't parse the instruction correctly. diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 683277993..2757314ab 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 94b4d919a..8be2e1894 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Lists - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 0f23555f4..e6736100f 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Parse - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index db58e9654..c0d8029dc 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -13,6 +13,8 @@ import Control.DeepSeq (NFData, deepseq) import Control.Monad (guard, liftM) import Control.Monad.Except (throwError) import qualified Data.ByteString.Lazy as BL (ByteString) +import qualified Data.Text.Lazy.Encoding as TL +import qualified Data.Text.Lazy as TL import Data.List (isInfixOf, isPrefixOf) import qualified Data.Map as M (Map, elems, fromList, lookup) import Data.Maybe (fromMaybe, mapMaybe) @@ -73,7 +75,7 @@ archiveToEPUB os archive = do mimeToReader "application/xhtml+xml" (unEscapeString -> root) (unEscapeString -> path) = do fname <- findEntryByPathE (root </> path) archive - html <- readHtml os' . UTF8.toStringLazy $ fromEntry fname + html <- readHtml os' . TL.toStrict . TL.decodeUtf8 $ fromEntry fname return $ fixInternalReferences path html mimeToReader s _ (unEscapeString -> path) | s `elem` imageMimes = return $ imageToPandoc path diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 14b051539..94f933c4d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, -ViewPatterns#-} +ViewPatterns, OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.HTML - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,6 +34,7 @@ module Text.Pandoc.Readers.HTML ( readHtml , htmlInBalanced , isInlineTag , isBlockTag + , NamedTag(..) , isTextTag , isCommentTag ) where @@ -43,7 +44,7 @@ import Text.HTML.TagSoup.Match import Text.Pandoc.Definition import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) -import Text.Pandoc.Shared ( extractSpaces, renderTags', addMetaField +import Text.Pandoc.Shared ( extractSpaces, addMetaField , escapeURI, safeRead ) import Text.Pandoc.Options (ReaderOptions(readerExtensions), extensionEnabled, Extension (Ext_epub_html_exts, @@ -53,12 +54,14 @@ import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk import qualified Data.Map as M import Data.Maybe ( fromMaybe, isJust) -import Data.List ( intercalate, isInfixOf, isPrefixOf ) +import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) import Control.Monad ( guard, mzero, void, unless ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) +import Data.Text (Text) +import qualified Data.Text as T import Text.TeXMath (readMathML, writeTeX) import Data.Default (Default (..), def) import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) @@ -74,11 +77,12 @@ import Control.Monad.Except (throwError) -- | Convert HTML-formatted string to 'Pandoc' document. readHtml :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assumes @'\n'@ line endings) + -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readHtml opts inp = do let tags = stripPrefixes . canonicalizeTags $ - parseTagsOptions parseOptions{ optTagPosition = True } inp + parseTagsOptions parseOptions{ optTagPosition = True } + inp parseDoc = do blocks <- (fixPlains False) . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState @@ -128,7 +132,7 @@ setInPlain = local (\s -> s {inPlain = True}) type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m) -type TagParser m = HTMLParser m [Tag String] +type TagParser m = HTMLParser m [Tag Text] pBody :: PandocMonad m => TagParser m Blocks pBody = pInTags "body" block @@ -138,12 +142,12 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag where pTitle = pInTags "title" inline >>= setTitle . trimInlines setTitle t = mempty <$ (updateState $ B.setMeta "title" t) pMetaTag = do - mt <- pSatisfy (~== TagOpen "meta" []) - let name = fromAttrib "name" mt + mt <- pSatisfy (matchTagOpen "meta" []) + let name = T.unpack $ fromAttrib "name" mt if null name then return mempty else do - let content = fromAttrib "content" mt + let content = T.unpack $ fromAttrib "content" mt updateState $ \s -> let ps = parserState s in s{ parserState = ps{ @@ -151,9 +155,9 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag (stateMeta ps) } } return mempty pBaseTag = do - bt <- pSatisfy (~== TagOpen "base" []) + bt <- pSatisfy (matchTagOpen "base" []) updateState $ \st -> st{ baseHref = - parseURIReference $ fromAttrib "href" bt } + parseURIReference $ T.unpack $ fromAttrib "href" bt } return mempty block :: PandocMonad m => TagParser m Blocks @@ -193,29 +197,31 @@ eSwitch :: (PandocMonad m, Monoid a) -> TagParser m a eSwitch constructor parser = try $ do guardEnabled Ext_epub_html_exts - pSatisfy (~== TagOpen "switch" []) + pSatisfy (matchTagOpen "switch" []) cases <- getFirst . mconcat <$> manyTill (First <$> (eCase <* skipMany pBlank) ) - (lookAhead $ try $ pSatisfy (~== TagOpen "default" [])) + (lookAhead $ try $ pSatisfy (matchTagOpen "default" [])) skipMany pBlank fallback <- pInTags "default" (skipMany pBlank *> parser <* skipMany pBlank) skipMany pBlank - pSatisfy (~== TagClose "switch") + pSatisfy (matchTagClose "switch") return $ maybe fallback constructor cases eCase :: PandocMonad m => TagParser m (Maybe Inlines) eCase = do skipMany pBlank - TagOpen _ attr <- lookAhead $ pSatisfy $ (~== TagOpen "case" []) + TagOpen _ attr' <- lookAhead $ pSatisfy $ (matchTagOpen "case" []) + let attr = toStringAttr attr' case (flip lookup namespaces) =<< lookup "required-namespace" attr of Just p -> Just <$> (pInTags "case" (skipMany pBlank *> p <* skipMany pBlank)) - Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (~== TagClose "case")) + Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (matchTagClose "case")) eFootnote :: PandocMonad m => TagParser m () eFootnote = try $ do let notes = ["footnote", "rearnote"] guardEnabled Ext_epub_html_exts - (TagOpen tag attr) <- lookAhead $ pAnyTag + (TagOpen tag attr') <- lookAhead $ pAnyTag + let attr = toStringAttr attr' guard (maybe False (flip elem notes) (lookup "type" attr)) let ident = fromMaybe "" (lookup "id" attr) content <- pInTags tag block @@ -227,7 +233,8 @@ addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : (noteTable s) eNoteref :: PandocMonad m => TagParser m Inlines eNoteref = try $ do guardEnabled Ext_epub_html_exts - TagOpen tag attr <- lookAhead $ pAnyTag + TagOpen tag attr' <- lookAhead $ pAnyTag + let attr = toStringAttr attr' guard (maybe False (== "noteref") (lookup "type" attr)) let ident = maybe "" (dropWhile (== '#')) (lookup "href" attr) guard (not (null ident)) @@ -247,10 +254,10 @@ pList = pBulletList <|> pOrderedList <|> pDefinitionList pBulletList :: PandocMonad m => TagParser m Blocks pBulletList = try $ do - pSatisfy (~== TagOpen "ul" []) + pSatisfy (matchTagOpen "ul" []) let nonItem = pSatisfy (\t -> not (tagOpen (`elem` ["li","ol","ul","dl"]) (const True) t) && - not (t ~== TagClose "ul")) + not (matchTagClose "ul" t)) -- note: if they have an <ol> or <ul> not in scope of a <li>, -- treat it as a list item, though it's not valid xhtml... skipMany nonItem @@ -259,7 +266,8 @@ pBulletList = try $ do pListItem :: PandocMonad m => TagParser m a -> TagParser m Blocks pListItem nonItem = do - TagOpen _ attr <- lookAhead $ pSatisfy (~== TagOpen "li" []) + TagOpen _ attr' <- lookAhead $ pSatisfy (matchTagOpen "li" []) + let attr = toStringAttr attr' let addId ident bs = case B.toList bs of (Plain ils:xs) -> B.fromList (Plain [Span (ident, [], []) ils] : xs) @@ -285,7 +293,8 @@ parseTypeAttr _ = DefaultStyle pOrderedList :: PandocMonad m => TagParser m Blocks pOrderedList = try $ do - TagOpen _ attribs <- pSatisfy (~== TagOpen "ol" []) + TagOpen _ attribs' <- pSatisfy (matchTagOpen "ol" []) + let attribs = toStringAttr attribs' let (start, style) = (sta', sty') where sta = fromMaybe "1" $ lookup "start" attribs @@ -307,7 +316,7 @@ pOrderedList = try $ do ] let nonItem = pSatisfy (\t -> not (tagOpen (`elem` ["li","ol","ul","dl"]) (const True) t) && - not (t ~== TagClose "ol")) + not (matchTagClose "ol" t)) -- note: if they have an <ol> or <ul> not in scope of a <li>, -- treat it as a list item, though it's not valid xhtml... skipMany nonItem @@ -316,14 +325,14 @@ pOrderedList = try $ do pDefinitionList :: PandocMonad m => TagParser m Blocks pDefinitionList = try $ do - pSatisfy (~== TagOpen "dl" []) + pSatisfy (matchTagOpen "dl" []) items <- manyTill pDefListItem (pCloses "dl") return $ B.definitionList items pDefListItem :: PandocMonad m => TagParser m (Inlines, [Blocks]) pDefListItem = try $ do - let nonItem = pSatisfy (\t -> not (t ~== TagOpen "dt" []) && - not (t ~== TagOpen "dd" []) && not (t ~== TagClose "dl")) + let nonItem = pSatisfy (\t -> not (matchTagOpen "dt" [] t) && + not (matchTagOpen "dd" [] t) && not (matchTagClose "dl" t)) terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline) defs <- many1 (try $ skipMany nonItem >> pInTags "dd" block) skipMany nonItem @@ -346,12 +355,12 @@ fixPlains inList bs = if any isParaish bs' plainToPara x = x bs' = B.toList bs -pRawTag :: PandocMonad m => TagParser m String +pRawTag :: PandocMonad m => TagParser m Text pRawTag = do tag <- pAnyTag let ignorable x = x `elem` ["html","head","body","!DOCTYPE","?xml"] if tagOpen ignorable (const True) tag || tagClose ignorable tag - then return [] + then return mempty else return $ renderTags' [tag] pDiv :: PandocMonad m => TagParser m Blocks @@ -360,7 +369,8 @@ pDiv = try $ do let isDivLike "div" = True isDivLike "section" = True isDivLike _ = False - TagOpen tag attr <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) + TagOpen tag attr' <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) + let attr = toStringAttr attr' contents <- pInTags tag block let (ident, classes, kvs) = mkAttr attr let classes' = if tag == "section" @@ -370,7 +380,7 @@ pDiv = try $ do pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do - raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag + raw <- T.unpack <$> (pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag) exts <- getOption readerExtensions if extensionEnabled Ext_raw_html exts && not (null raw) then return $ B.rawBlock "html" raw @@ -385,33 +395,35 @@ ignore raw = do logMessage $ SkippedContent raw pos return mempty -pHtmlBlock :: PandocMonad m => String -> TagParser m String +pHtmlBlock :: PandocMonad m => Text -> TagParser m Text pHtmlBlock t = try $ do - open <- pSatisfy (~== TagOpen t []) - contents <- manyTill pAnyTag (pSatisfy (~== TagClose t)) - return $ renderTags' $ [open] ++ contents ++ [TagClose t] + open <- pSatisfy (matchTagOpen t []) + contents <- manyTill pAnyTag (pSatisfy (matchTagClose t)) + return $ renderTags' $ [open] <> contents <> [TagClose t] -- Sets chapter context eSection :: PandocMonad m => TagParser m Blocks eSection = try $ do - let matchChapter as = maybe False (isInfixOf "chapter") (lookup "type" as) + let matchChapter as = maybe False (T.isInfixOf "chapter") (lookup "type" as) let sectTag = tagOpen (`elem` sectioningContent) matchChapter TagOpen tag _ <- lookAhead $ pSatisfy sectTag setInChapter (pInTags tag block) -headerLevel :: PandocMonad m => String -> TagParser m Int +headerLevel :: PandocMonad m => Text -> TagParser m Int headerLevel tagtype = do - let level = read (drop 1 tagtype) - (try $ do - guardEnabled Ext_epub_html_exts - asks inChapter >>= guard - return (level - 1)) - <|> - return level + case safeRead (T.unpack (T.drop 1 tagtype)) of + Just level -> + (try $ do + guardEnabled Ext_epub_html_exts + asks inChapter >>= guard + return (level - 1)) + <|> + return level + Nothing -> fail "Could not retrieve header level" eTitlePage :: PandocMonad m => TagParser m () eTitlePage = try $ do - let isTitlePage as = maybe False (isInfixOf "titlepage") (lookup "type" as) + let isTitlePage as = maybe False (T.isInfixOf "titlepage") (lookup "type" as) let groupTag = tagOpen (\x -> x `elem` groupingContent || x == "section") isTitlePage TagOpen tag _ <- lookAhead $ pSatisfy groupTag @@ -419,19 +431,21 @@ eTitlePage = try $ do pHeader :: PandocMonad m => TagParser m Blocks pHeader = try $ do - TagOpen tagtype attr <- pSatisfy $ + TagOpen tagtype attr' <- pSatisfy $ tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"]) (const True) - let bodyTitle = TagOpen tagtype attr ~== TagOpen "h1" [("class","title")] + let attr = toStringAttr attr' + let bodyTitle = TagOpen tagtype attr' ~== TagOpen ("h1" :: Text) + [("class","title")] level <- headerLevel tagtype contents <- trimInlines . mconcat <$> manyTill inline (pCloses tagtype <|> eof) let ident = fromMaybe "" $ lookup "id" attr let classes = maybe [] words $ lookup "class" attr let keyvals = [(k,v) | (k,v) <- attr, k /= "class", k /= "id"] - attr' <- registerHeader (ident, classes, keyvals) contents + attr'' <- registerHeader (ident, classes, keyvals) contents return $ if bodyTitle then mempty -- skip a representation of the title in the body - else B.headerWith attr' level contents + else B.headerWith attr'' level contents pHrule :: PandocMonad m => TagParser m Blocks pHrule = do @@ -440,7 +454,7 @@ pHrule = do pTable :: PandocMonad m => TagParser m Blocks pTable = try $ do - TagOpen _ _ <- pSatisfy (~== TagOpen "table" []) + TagOpen _ _ <- pSatisfy (matchTagOpen "table" []) skipMany pBlank caption <- option mempty $ pInTags "caption" inline <* skipMany pBlank widths' <- (mconcat <$> many1 pColgroup) <|> many pCol @@ -454,8 +468,8 @@ pTable = try $ do else return head'' rowsLs <- many pTBody rows' <- pOptInTag "tfoot" $ many pTr - TagClose _ <- pSatisfy (~== TagClose "table") - let rows'' = (concat rowsLs) ++ rows' + TagClose _ <- pSatisfy (matchTagClose "table") + let rows'' = (concat rowsLs) <> rows' -- fail on empty table guard $ not $ null head' && null rows'' let isSinglePlain x = case B.toList x of @@ -466,7 +480,7 @@ pTable = try $ do let cols = length $ if null head' then head rows'' else head' -- add empty cells to short rows let addEmpties r = case cols - length r of - n | n > 0 -> r ++ replicate n mempty + n | n > 0 -> r <> replicate n mempty | otherwise -> r let rows = map addEmpties rows'' let aligns = replicate cols AlignDefault @@ -479,15 +493,16 @@ pTable = try $ do pCol :: PandocMonad m => TagParser m Double pCol = try $ do - TagOpen _ attribs <- pSatisfy (~== TagOpen "col" []) + TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" []) + let attribs = toStringAttr attribs' skipMany pBlank - optional $ pSatisfy (~== TagClose "col") + optional $ pSatisfy (matchTagClose "col") skipMany pBlank return $ case lookup "width" attribs of Nothing -> case lookup "style" attribs of Just ('w':'i':'d':'t':'h':':':xs) | '%' `elem` xs -> fromMaybe 0.0 $ safeRead ('0':'.':filter - (`notElem` " \t\r\n%'\";") xs) + (`notElem` (" \t\r\n%'\";" :: [Char])) xs) _ -> 0.0 Just x | not (null x) && last x == '%' -> fromMaybe 0.0 $ safeRead ('0':'.':init x) @@ -495,18 +510,18 @@ pCol = try $ do pColgroup :: PandocMonad m => TagParser m [Double] pColgroup = try $ do - pSatisfy (~== TagOpen "colgroup" []) + pSatisfy (matchTagOpen "colgroup" []) skipMany pBlank manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank -noColOrRowSpans :: Tag String -> Bool +noColOrRowSpans :: Tag Text -> Bool noColOrRowSpans t = isNullOrOne "colspan" && isNullOrOne "rowspan" where isNullOrOne x = case fromAttrib x t of "" -> True "1" -> True _ -> False -pCell :: PandocMonad m => String -> TagParser m [Blocks] +pCell :: PandocMonad m => Text -> TagParser m [Blocks] pCell celltype = try $ do skipMany pBlank res <- pInTags' celltype noColOrRowSpans block @@ -532,7 +547,8 @@ pPara = do pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do - TagOpen _ attr <- pSatisfy (~== TagOpen "pre" []) + TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" []) + let attr = toStringAttr attr' contents <- manyTill pAnyTag (pCloses "pre" <|> eof) let rawText = concatMap tagToString contents -- drop leading newline if any @@ -545,8 +561,8 @@ pCodeBlock = try $ do _ -> result' return $ B.codeBlockWith (mkAttr attr) result -tagToString :: Tag String -> String -tagToString (TagText s) = s +tagToString :: Tag Text -> String +tagToString (TagText s) = T.unpack s tagToString (TagOpen "br" _) = "\n" tagToString _ = "" @@ -575,20 +591,20 @@ pLocation = do (TagPosition r c) <- pSat isTagPosition setPosition $ newPos "input" r c -pSat :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) +pSat :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text) pSat f = do pos <- getPosition token show (const pos) (\x -> if f x then Just x else Nothing) -pSatisfy :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) +pSatisfy :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text) pSatisfy f = try $ optional pLocation >> pSat f -pAnyTag :: PandocMonad m => TagParser m (Tag String) +pAnyTag :: PandocMonad m => TagParser m (Tag Text) pAnyTag = pSatisfy (const True) pSelfClosing :: PandocMonad m - => (String -> Bool) -> ([Attribute String] -> Bool) - -> TagParser m (Tag String) + => (Text -> Bool) -> ([Attribute Text] -> Bool) + -> TagParser m (Tag Text) pSelfClosing f g = do open <- pSatisfy (tagOpen f g) optional $ pSatisfy (tagClose f) @@ -626,7 +642,7 @@ pStrikeout = do pInlinesInTags "s" B.strikeout <|> pInlinesInTags "strike" B.strikeout <|> pInlinesInTags "del" B.strikeout <|> - try (do pSatisfy (~== TagOpen "span" [("class","strikeout")]) + try (do pSatisfy (matchTagOpen "span" [("class","strikeout")]) contents <- mconcat <$> manyTill inline (pCloses "span") return $ B.strikeout contents) @@ -637,17 +653,19 @@ pLineBreak = do -- Unlike fromAttrib from tagsoup, this distinguishes -- between a missing attribute and an attribute with empty content. -maybeFromAttrib :: String -> Tag String -> Maybe String -maybeFromAttrib name (TagOpen _ attrs) = lookup name attrs +maybeFromAttrib :: String -> Tag Text -> Maybe String +maybeFromAttrib name (TagOpen _ attrs) = + T.unpack <$> lookup (T.pack name) attrs maybeFromAttrib _ _ = Nothing pLink :: PandocMonad m => TagParser m Inlines pLink = try $ do tag <- pSatisfy $ tagOpenLit "a" (const True) - let title = fromAttrib "title" tag + let title = T.unpack $ fromAttrib "title" tag -- take id from id attribute if present, otherwise name - let uid = maybe (fromAttrib "name" tag) id $ maybeFromAttrib "id" tag - let cls = words $ fromAttrib "class" tag + let uid = maybe (T.unpack $ fromAttrib "name" tag) id $ + maybeFromAttrib "id" tag + let cls = words $ T.unpack $ fromAttrib "class" tag lab <- trimInlines . mconcat <$> manyTill inline (pCloses "a") -- check for href; if href, then a link, otherwise a span case maybeFromAttrib "href" tag of @@ -665,30 +683,33 @@ pImage :: PandocMonad m => TagParser m Inlines pImage = do tag <- pSelfClosing (=="img") (isJust . lookup "src") mbBaseHref <- baseHref <$> getState - let url' = fromAttrib "src" tag + let url' = T.unpack $ fromAttrib "src" tag let url = case (parseURIReference url', mbBaseHref) of (Just rel, Just bs) -> show (rel `nonStrictRelativeTo` bs) _ -> url' - let title = fromAttrib "title" tag - let alt = fromAttrib "alt" tag - let uid = fromAttrib "id" tag - let cls = words $ fromAttrib "class" tag + let title = T.unpack $ fromAttrib "title" tag + let alt = T.unpack $ fromAttrib "alt" tag + let uid = T.unpack $ fromAttrib "id" tag + let cls = words $ T.unpack $ fromAttrib "class" tag let getAtt k = case fromAttrib k tag of "" -> [] - v -> [(k, v)] + v -> [(T.unpack k, T.unpack v)] let kvs = concat $ map getAtt ["width", "height", "sizes", "srcset"] return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt) pCode :: PandocMonad m => TagParser m Inlines pCode = try $ do - (TagOpen open attr) <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) + (TagOpen open attr') <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) + let attr = toStringAttr attr' result <- manyTill pAnyTag (pCloses open) - return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ innerText result + return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ T.unpack $ + innerText result pSpan :: PandocMonad m => TagParser m Inlines pSpan = try $ do guardEnabled Ext_native_spans - TagOpen _ attr <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True) + TagOpen _ attr' <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True) + let attr = toStringAttr attr' contents <- pInTags "span" inline let isSmallCaps = fontVariant == "small-caps" || "smallcaps" `elem` classes where styleAttr = fromMaybe "" $ lookup "style" attr @@ -706,7 +727,7 @@ pRawHtmlInline = do then pSatisfy (not . isBlockTag) else pSatisfy isInlineTag exts <- getOption readerExtensions - let raw = renderTags' [result] + let raw = T.unpack $ renderTags' [result] if extensionEnabled Ext_raw_html exts then return $ B.rawInline "html" raw else ignore raw @@ -714,32 +735,38 @@ pRawHtmlInline = do mathMLToTeXMath :: String -> Either String String mathMLToTeXMath s = writeTeX <$> readMathML s +toStringAttr :: [(Text, Text)] -> [(String, String)] +toStringAttr = map go + where go (x,y) = (T.unpack x, T.unpack y) + pMath :: PandocMonad m => Bool -> TagParser m Inlines pMath inCase = try $ do - open@(TagOpen _ attr) <- pSatisfy $ tagOpen (=="math") (const True) + open@(TagOpen _ attr') <- pSatisfy $ tagOpen (=="math") (const True) -- we'll assume math tags are MathML unless specially marked -- otherwise... + let attr = toStringAttr attr' unless inCase $ guard (maybe True (== mathMLNamespace) (lookup "xmlns" attr)) - contents <- manyTill pAnyTag (pSatisfy (~== TagClose "math")) - case mathMLToTeXMath (renderTags $ [open] ++ contents ++ [TagClose "math"]) of + contents <- manyTill pAnyTag (pSatisfy (matchTagClose "math")) + case mathMLToTeXMath (T.unpack $ renderTags $ + [open] <> contents <> [TagClose "math"]) of Left _ -> return $ B.spanWith ("",["math"],attr) $ B.text $ - innerText contents + T.unpack $ innerText contents Right [] -> return mempty Right x -> return $ case lookup "display" attr of Just "block" -> B.displayMath x _ -> B.math x -pInlinesInTags :: PandocMonad m => String -> (Inlines -> Inlines) +pInlinesInTags :: PandocMonad m => Text -> (Inlines -> Inlines) -> TagParser m Inlines pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline -pInTags :: (PandocMonad m, Monoid a) => String -> TagParser m a -> TagParser m a +pInTags :: (PandocMonad m, Monoid a) => Text -> TagParser m a -> TagParser m a pInTags tagtype parser = pInTags' tagtype (const True) parser pInTags' :: (PandocMonad m, Monoid a) - => String - -> (Tag String -> Bool) + => Text + -> (Tag Text -> Bool) -> TagParser m a -> TagParser m a pInTags' tagtype tagtest parser = try $ do @@ -748,18 +775,18 @@ pInTags' tagtype tagtest parser = try $ do -- parses p, preceeded by an optional opening tag -- and followed by an optional closing tags -pOptInTag :: PandocMonad m => String -> TagParser m a -> TagParser m a +pOptInTag :: PandocMonad m => Text -> TagParser m a -> TagParser m a pOptInTag tagtype p = try $ do skipMany pBlank - optional $ pSatisfy (~== TagOpen tagtype []) + optional $ pSatisfy (matchTagOpen tagtype []) skipMany pBlank x <- p skipMany pBlank - optional $ pSatisfy (~== TagClose tagtype) + optional $ pSatisfy (matchTagClose tagtype) skipMany pBlank return x -pCloses :: PandocMonad m => String -> TagParser m () +pCloses :: PandocMonad m => Text -> TagParser m () pCloses tagtype = try $ do t <- lookAhead $ pSatisfy $ \tag -> isTagClose tag || isTagOpen tag case t of @@ -780,15 +807,15 @@ pTagText = try $ do parsed <- lift $ lift $ flip runReaderT qu $ runParserT (many pTagContents) st "text" str case parsed of - Left _ -> throwError $ PandocParseError $ "Could not parse `" ++ str ++ "'" + Left _ -> throwError $ PandocParseError $ "Could not parse `" <> T.unpack str <> "'" Right result -> return $ mconcat result pBlank :: PandocMonad m => TagParser m () pBlank = try $ do (TagText str) <- pSatisfy isTagText - guard $ all isSpace str + guard $ T.all isSpace str -type InlinesParser m = HTMLParser m String +type InlinesParser m = HTMLParser m Text pTagContents :: PandocMonad m => InlinesParser m Inlines pTagContents = @@ -869,80 +896,89 @@ pSpace = many1 (satisfy isSpace) >>= \xs -> -- Constants -- -eitherBlockOrInline :: [String] -eitherBlockOrInline = ["audio", "applet", "button", "iframe", "embed", - "del", "ins", - "progress", "map", "area", "noscript", "script", - "object", "svg", "video", "source"] - -{- -inlineHtmlTags :: [[Char]] -inlineHtmlTags = ["a", "abbr", "acronym", "b", "basefont", "bdo", "big", - "br", "cite", "code", "dfn", "em", "font", "i", "img", - "input", "kbd", "label", "q", "s", "samp", "select", - "small", "span", "strike", "strong", "sub", "sup", - "textarea", "tt", "u", "var"] --} - -blockHtmlTags :: [String] -blockHtmlTags = ["?xml", "!DOCTYPE", "address", "article", "aside", - "blockquote", "body", "button", "canvas", - "caption", "center", "col", "colgroup", "dd", "dir", "div", - "dl", "dt", "fieldset", "figcaption", "figure", - "footer", "form", "h1", "h2", "h3", "h4", - "h5", "h6", "head", "header", "hgroup", "hr", "html", - "isindex", "menu", "noframes", "ol", "output", "p", "pre", - "section", "table", "tbody", "textarea", - "thead", "tfoot", "ul", "dd", - "dt", "frameset", "li", "tbody", "td", "tfoot", - "th", "thead", "tr", "script", "style"] +eitherBlockOrInline :: Set.Set Text +eitherBlockOrInline = Set.fromList + ["audio", "applet", "button", "iframe", "embed", + "del", "ins", "progress", "map", "area", "noscript", "script", + "object", "svg", "video", "source"] + +blockHtmlTags :: Set.Set Text +blockHtmlTags = Set.fromList + ["?xml", "!DOCTYPE", "address", "article", "aside", + "blockquote", "body", "canvas", + "caption", "center", "col", "colgroup", "dd", "details", + "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", + "footer", "form", "h1", "h2", "h3", "h4", + "h5", "h6", "head", "header", "hgroup", "hr", "html", + "isindex", "menu", "noframes", "ol", "output", "p", "pre", + "section", "table", "tbody", "textarea", + "thead", "tfoot", "ul", "dd", + "dt", "frameset", "li", "tbody", "td", "tfoot", + "th", "thead", "tr", "script", "style"] -- We want to allow raw docbook in markdown documents, so we -- include docbook block tags here too. -blockDocBookTags :: [String] -blockDocBookTags = ["calloutlist", "bibliolist", "glosslist", "itemizedlist", - "orderedlist", "segmentedlist", "simplelist", - "variablelist", "caution", "important", "note", "tip", - "warning", "address", "literallayout", "programlisting", - "programlistingco", "screen", "screenco", "screenshot", - "synopsis", "example", "informalexample", "figure", - "informalfigure", "table", "informaltable", "para", - "simpara", "formalpara", "equation", "informalequation", - "figure", "screenshot", "mediaobject", "qandaset", - "procedure", "task", "cmdsynopsis", "funcsynopsis", - "classsynopsis", "blockquote", "epigraph", "msgset", - "sidebar", "title"] - -epubTags :: [String] -epubTags = ["case", "switch", "default"] - -blockTags :: [String] -blockTags = blockHtmlTags ++ blockDocBookTags ++ epubTags - -isInlineTag :: Tag String -> Bool -isInlineTag t = tagOpen isInlineTagName (const True) t || - tagClose isInlineTagName t || - tagComment (const True) t - where isInlineTagName x = x `notElem` blockTags - -isBlockTag :: Tag String -> Bool -isBlockTag t = tagOpen isBlockTagName (const True) t || - tagClose isBlockTagName t || - tagComment (const True) t - where isBlockTagName ('?':_) = True - isBlockTagName ('!':_) = True - isBlockTagName x = x `elem` blockTags - || x `elem` eitherBlockOrInline - -isTextTag :: Tag String -> Bool +blockDocBookTags :: Set.Set Text +blockDocBookTags = Set.fromList + ["calloutlist", "bibliolist", "glosslist", "itemizedlist", + "orderedlist", "segmentedlist", "simplelist", + "variablelist", "caution", "important", "note", "tip", + "warning", "address", "literallayout", "programlisting", + "programlistingco", "screen", "screenco", "screenshot", + "synopsis", "example", "informalexample", "figure", + "informalfigure", "table", "informaltable", "para", + "simpara", "formalpara", "equation", "informalequation", + "figure", "screenshot", "mediaobject", "qandaset", + "procedure", "task", "cmdsynopsis", "funcsynopsis", + "classsynopsis", "blockquote", "epigraph", "msgset", + "sidebar", "title"] + +epubTags :: Set.Set Text +epubTags = Set.fromList ["case", "switch", "default"] + +blockTags :: Set.Set Text +blockTags = Set.unions [blockHtmlTags, blockDocBookTags, epubTags] + +class NamedTag a where + getTagName :: a -> Maybe Text + +instance NamedTag (Tag Text) where + getTagName (TagOpen t _) = Just t + getTagName (TagClose t) = Just t + getTagName _ = Nothing + +instance NamedTag (Tag String) where + getTagName (TagOpen t _) = Just (T.pack t) + getTagName (TagClose t) = Just (T.pack t) + getTagName _ = Nothing + +isInlineTag :: NamedTag (Tag a) => Tag a -> Bool +isInlineTag t = isInlineTagName || isCommentTag t + where isInlineTagName = case getTagName t of + Just x -> x + `Set.notMember` blockTags + Nothing -> False + +isBlockTag :: NamedTag (Tag a) => Tag a -> Bool +isBlockTag t = isBlockTagName || isTagComment t + where isBlockTagName = + case getTagName t of + Just x + | "?" `T.isPrefixOf` x -> True + | "!" `T.isPrefixOf` x -> True + | otherwise -> x `Set.member` blockTags + || x `Set.member` eitherBlockOrInline + Nothing -> False + +isTextTag :: Tag a -> Bool isTextTag = tagText (const True) -isCommentTag :: Tag String -> Bool +isCommentTag :: Tag a -> Bool isCommentTag = tagComment (const True) -- taken from HXT and extended -- See http://www.w3.org/TR/html5/syntax.html sec 8.1.2.4 optional tags -closes :: String -> String -> Bool +closes :: Text -> Text -> Bool _ `closes` "body" = False _ `closes` "html" = False "body" `closes` "head" = True @@ -975,8 +1011,9 @@ t `closes` t2 | t `elem` ["h1","h2","h3","h4","h5","h6","dl","ol","ul","table","div","p"] && t2 `elem` ["h1","h2","h3","h4","h5","h6","p" ] = True -- not "div" t1 `closes` t2 | - t1 `elem` blockTags && - t2 `notElem` (blockTags ++ eitherBlockOrInline) = True + t1 `Set.member` blockTags && + t2 `Set.notMember` blockTags && + t2 `Set.notMember` eitherBlockOrInline = True _ `closes` _ = False --- parsers for use in markdown, textile readers @@ -1003,8 +1040,11 @@ htmlInBalanced f = try $ do let cs = ec - sc lscontents <- unlines <$> count ls anyLine cscontents <- count cs anyChar - (_,closetag) <- htmlTag (~== TagClose tn) - return (lscontents ++ cscontents ++ closetag) + closetag <- do + x <- many (satisfy (/='>')) + char '>' + return (x <> ">") + return (lscontents <> cscontents <> closetag) _ -> mzero _ -> mzero @@ -1022,7 +1062,7 @@ htmlInBalanced' tagname ts = fromMaybe [] $ go 0 ts go n (t:ts') = (t :) <$> go n ts' go _ [] = mzero -hasTagWarning :: [Tag String] -> Bool +hasTagWarning :: [Tag a] -> Bool hasTagWarning (TagWarning _:_) = True hasTagWarning _ = False @@ -1050,47 +1090,48 @@ htmlTag f = try $ do -- basic sanity check, since the parser is very forgiving -- and finds tags in stuff like x<y) guard $ isName tagname + guard $ not $ null tagname -- <https://example.org> should NOT be a tag either. -- tagsoup will parse it as TagOpen "https:" [("example.org","")] guard $ last tagname /= ':' rendered <- manyTill anyChar (char '>') - return (next, rendered ++ ">") + return (next, rendered <> ">") case next of TagComment s | "<!--" `isPrefixOf` inp -> do count (length s + 4) anyChar skipMany (satisfy (/='>')) char '>' - return (next, "<!--" ++ s ++ "-->") + return (next, "<!--" <> s <> "-->") | otherwise -> fail "bogus comment mode, HTML5 parse error" TagOpen tagname attr -> do guard $ all (isName . fst) attr handleTag tagname - TagClose tagname -> handleTag tagname + TagClose tagname -> + handleTag tagname _ -> mzero mkAttr :: [(String, String)] -> Attr mkAttr attr = (attribsId, attribsClasses, attribsKV) where attribsId = fromMaybe "" $ lookup "id" attr - attribsClasses = (words $ fromMaybe "" $ lookup "class" attr) ++ epubTypes + attribsClasses = (words $ fromMaybe "" $ lookup "class" attr) <> epubTypes attribsKV = filter (\(k,_) -> k /= "class" && k /= "id") attr epubTypes = words $ fromMaybe "" $ lookup "epub:type" attr -- Strip namespace prefixes -stripPrefixes :: [Tag String] -> [Tag String] +stripPrefixes :: [Tag Text] -> [Tag Text] stripPrefixes = map stripPrefix -stripPrefix :: Tag String -> Tag String +stripPrefix :: Tag Text -> Tag Text stripPrefix (TagOpen s as) = TagOpen (stripPrefix' s) (map (stripPrefix' *** id) as) stripPrefix (TagClose s) = TagClose (stripPrefix' s) stripPrefix x = x -stripPrefix' :: String -> String +stripPrefix' :: Text -> Text stripPrefix' s = - case span (/= ':') s of - (_, "") -> s - (_, (_:ts)) -> ts + if T.null t then s else T.drop 1 t + where (_, t) = T.span (/= ':') s isSpace :: Char -> Bool isSpace ' ' = True @@ -1133,19 +1174,32 @@ instance HasLastStrPosition HTMLState where setLastStrPos s st = st {parserState = setLastStrPos s (parserState st)} getLastStrPos = getLastStrPos . parserState +-- For now we need a special verison here; the one in Shared has String type +renderTags' :: [Tag Text] -> Text +renderTags' = renderTagsOptions + renderOptions{ optMinimize = matchTags ["hr", "br", "img", + "meta", "link"] + , optRawTag = matchTags ["script", "style"] } + where matchTags = \tags -> flip elem tags . T.toLower + -- EPUB Specific -- -- -sectioningContent :: [String] +sectioningContent :: [Text] sectioningContent = ["article", "aside", "nav", "section"] -groupingContent :: [String] +groupingContent :: [Text] groupingContent = ["p", "hr", "pre", "blockquote", "ol" , "ul", "li", "dl", "dt", "dt", "dd" , "figure", "figcaption", "div", "main"] +matchTagClose :: Text -> (Tag Text -> Bool) +matchTagClose t = (~== TagClose t) + +matchTagOpen :: Text -> [(Text, Text)] -> (Tag Text -> Bool) +matchTagOpen t as = (~== TagOpen t as) {- @@ -1153,7 +1207,7 @@ types :: [(String, ([String], Int))] types = -- Document divisions map (\s -> (s, (["section", "body"], 0))) ["volume", "part", "chapter", "division"] - ++ -- Document section and components + <> -- Document section and components [ ("abstract", ([], 0))] -} diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 28caa528e..b22b71b96 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -16,6 +16,7 @@ module Text.Pandoc.Readers.Haddock import Control.Monad.Except (throwError) import Data.List (intersperse, stripPrefix) +import Data.Text (Text, unpack) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Documentation.Haddock.Parser @@ -32,9 +33,9 @@ import Text.Pandoc.Shared (splitBy, trim) -- | Parse Haddock markup and return a 'Pandoc' document. readHaddock :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc -readHaddock opts s = case readHaddockEither opts s of +readHaddock opts s = case readHaddockEither opts (unpack s) of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1d13f7107..17fb48548 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.LaTeX - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -39,6 +39,7 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Data.Char (chr, isAlphaNum, isLetter, ord) +import Data.Text (Text, unpack) import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList) @@ -46,7 +47,7 @@ import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs, - report, setResourcePath) + report, setResourcePath, getResourcePath) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging @@ -59,10 +60,10 @@ import Text.Pandoc.Walk -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assumes @'\n'@ line endings) + -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readLaTeX opts ltx = do - parsed <- readWithM parseLaTeX def{ stateOptions = opts } ltx + parsed <- readWithM parseLaTeX def{ stateOptions = opts } (unpack ltx) case parsed of Right result -> return result Left e -> throwError e @@ -276,8 +277,6 @@ block = (mempty <$ comment) <|> blockCommand <|> paragraph <|> grouped block - <|> (mempty <$ char '&') -- loose & in table environment - blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block @@ -304,8 +303,8 @@ blockCommand = try $ do rawcommand <- getRawCommand name' transformed <- applyMacros' rawcommand guard $ transformed /= rawcommand - notFollowedBy $ parseFromString inlines transformed - parseFromString blocks transformed + notFollowedBy $ parseFromString' inlines transformed + parseFromString' blocks transformed lookupListDefault raw [name',name] blockCommands inBrackets :: Inlines -> Inlines @@ -432,7 +431,7 @@ coloredBlock stylename = do graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do ps <- bgroup *> (manyTill braced egroup) - setResourcePath (".":ps) + getResourcePath >>= setResourcePath . (++ ps) return mempty addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m () @@ -490,16 +489,19 @@ inlineCommand = try $ do transformed <- applyMacros' rawcommand exts <- getOption readerExtensions if transformed /= rawcommand - then parseFromString inlines transformed + then parseFromString' inlines transformed else if extensionEnabled Ext_raw_tex exts then return $ rawInline "latex" rawcommand else ignore rawcommand (lookupListDefault raw [name',name] inlineCommands <* optional (try (string "{}"))) -unlessParseRaw :: PandocMonad m => LP m () -unlessParseRaw = getOption readerExtensions >>= - guard . not . extensionEnabled Ext_raw_tex +rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines +rawInlineOr name' fallback = do + parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions + if parseRaw + then rawInline "latex" <$> getRawCommand name' + else fallback isBlockCommand :: String -> Bool isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks)) @@ -507,20 +509,20 @@ isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Bl inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines) inlineEnvironments = M.fromList - [ ("displaymath", mathEnv id Nothing "displaymath") - , ("math", math <$> verbEnv "math") - , ("equation", mathEnv id Nothing "equation") - , ("equation*", mathEnv id Nothing "equation*") - , ("gather", mathEnv id (Just "gathered") "gather") - , ("gather*", mathEnv id (Just "gathered") "gather*") - , ("multline", mathEnv id (Just "gathered") "multline") - , ("multline*", mathEnv id (Just "gathered") "multline*") - , ("eqnarray", mathEnv id (Just "aligned") "eqnarray") - , ("eqnarray*", mathEnv id (Just "aligned") "eqnarray*") - , ("align", mathEnv id (Just "aligned") "align") - , ("align*", mathEnv id (Just "aligned") "align*") - , ("alignat", mathEnv id (Just "aligned") "alignat") - , ("alignat*", mathEnv id (Just "aligned") "alignat*") + [ ("displaymath", mathEnvWith id Nothing "displaymath") + , ("math", math <$> mathEnv "math") + , ("equation", mathEnvWith id Nothing "equation") + , ("equation*", mathEnvWith id Nothing "equation*") + , ("gather", mathEnvWith id (Just "gathered") "gather") + , ("gather*", mathEnvWith id (Just "gathered") "gather*") + , ("multline", mathEnvWith id (Just "gathered") "multline") + , ("multline*", mathEnvWith id (Just "gathered") "multline*") + , ("eqnarray", mathEnvWith id (Just "aligned") "eqnarray") + , ("eqnarray*", mathEnvWith id (Just "aligned") "eqnarray*") + , ("align", mathEnvWith id (Just "aligned") "align") + , ("align*", mathEnvWith id (Just "aligned") "align*") + , ("alignat", mathEnvWith id (Just "aligned") "alignat") + , ("alignat*", mathEnvWith id (Just "aligned") "alignat*") ] inlineCommands :: PandocMonad m => M.Map String (LP m Inlines) @@ -547,11 +549,11 @@ inlineCommands = M.fromList $ , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") - , ("label", unlessParseRaw >> (inBrackets <$> tok)) - , ("ref", unlessParseRaw >> (inBrackets <$> tok)) + , ("label", rawInlineOr "label" (inBrackets <$> tok)) + , ("ref", rawInlineOr "ref" (inBrackets <$> tok)) , ("textgreek", tok) , ("sep", lit ",") - , ("cref", unlessParseRaw >> (inBrackets <$> tok)) -- from cleveref.sty + , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty , ("(", mathInline $ manyTill anyChar (try $ string "\\)")) , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]")) , ("ensuremath", mathInline braced) @@ -605,7 +607,7 @@ inlineCommands = M.fromList $ , ("u", option (str "u") $ try $ tok >>= accent breve) , ("i", lit "i") , ("\\", linebreak <$ (optional (bracketed inline) *> spaces')) - , (",", pure mempty) + , (",", lit "\8198") , ("@", pure mempty) , (" ", lit "\160") , ("ps", pure $ str "PS." <> space) @@ -698,6 +700,9 @@ inlineCommands = M.fromList $ -- LaTeX colors , ("textcolor", coloredInline "color") , ("colorbox", coloredInline "background-color") + -- fontawesome + , ("faCheck", lit "\10003") + , ("faClose", lit "\10007") ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -1045,7 +1050,7 @@ rawEnv name = do (bs, raw) <- withRaw $ env name blocks raw' <- applyMacros' $ beginCommand ++ raw if raw' /= beginCommand ++ raw - then parseFromString blocks raw' + then parseFromString' blocks raw' else if parseRaw then return $ rawBlock "latex" $ beginCommand ++ raw' else do @@ -1055,6 +1060,19 @@ rawEnv name = do report $ SkippedContent ("\\end{" ++ name ++ "}") pos2 return bs +rawVerbEnv :: PandocMonad m => String -> LP m Blocks +rawVerbEnv name = do + pos <- getPosition + (_, raw) <- withRaw $ verbEnv name + let raw' = "\\begin{tikzpicture}" ++ raw + exts <- getOption readerExtensions + let parseRaw = extensionEnabled Ext_raw_tex exts + if parseRaw + then return $ rawBlock "latex" raw' + else do + report $ SkippedContent raw' pos + return mempty + ---- maybeAddExtension :: String -> FilePath -> FilePath @@ -1119,7 +1137,7 @@ parseListingsOptions options = keyval :: PandocMonad m => LP m (String, String) keyval = try $ do key <- many1 alphaNum - val <- option "" $ char '=' >> many1 (alphaNum <|> char '.' <|> char '\\') + val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\")) skipMany spaceChar optional (char ',') skipMany spaceChar @@ -1130,7 +1148,7 @@ keyvals :: PandocMonad m => LP m [(String, String)] keyvals = try $ char '[' *> manyTill keyval (char ']') alltt :: PandocMonad m => String -> LP m Blocks -alltt t = walk strToCode <$> parseFromString blocks +alltt t = walk strToCode <$> parseFromString' blocks (substitute " " "\\ " $ substitute "%" "\\%" $ intercalate "\\\\\n" $ lines t) where strToCode (Str s) = Code nullAttr s @@ -1176,11 +1194,12 @@ environments = M.fromList , ("subfigure", env "subfigure" $ skipopts *> tok *> figure) , ("center", env "center" blocks) , ("longtable", env "longtable" $ - resetCaption *> simpTable False >>= addTableCaption) + resetCaption *> simpTable "longtable" False >>= addTableCaption) , ("table", env "table" $ resetCaption *> skipopts *> blocks >>= addTableCaption) - , ("tabular*", env "tabular" $ simpTable True) - , ("tabular", env "tabular" $ simpTable False) + , ("tabular*", env "tabular" $ simpTable "tabular*" True) + , ("tabularx", env "tabularx" $ simpTable "tabularx" True) + , ("tabular", env "tabular" $ simpTable "tabular" False) , ("quote", blockQuote <$> env "quote" blocks) , ("quotation", blockQuote <$> env "quotation" blocks) , ("verse", blockQuote <$> env "verse" blocks) @@ -1210,19 +1229,20 @@ environments = M.fromList , ("obeylines", parseFromString (para . trimInlines . mconcat <$> many inline) =<< intercalate "\\\\\n" . lines <$> verbEnv "obeylines") - , ("displaymath", mathEnv para Nothing "displaymath") - , ("equation", mathEnv para Nothing "equation") - , ("equation*", mathEnv para Nothing "equation*") - , ("gather", mathEnv para (Just "gathered") "gather") - , ("gather*", mathEnv para (Just "gathered") "gather*") - , ("multline", mathEnv para (Just "gathered") "multline") - , ("multline*", mathEnv para (Just "gathered") "multline*") - , ("eqnarray", mathEnv para (Just "aligned") "eqnarray") - , ("eqnarray*", mathEnv para (Just "aligned") "eqnarray*") - , ("align", mathEnv para (Just "aligned") "align") - , ("align*", mathEnv para (Just "aligned") "align*") - , ("alignat", mathEnv para (Just "aligned") "alignat") - , ("alignat*", mathEnv para (Just "aligned") "alignat*") + , ("displaymath", mathEnvWith para Nothing "displaymath") + , ("equation", mathEnvWith para Nothing "equation") + , ("equation*", mathEnvWith para Nothing "equation*") + , ("gather", mathEnvWith para (Just "gathered") "gather") + , ("gather*", mathEnvWith para (Just "gathered") "gather*") + , ("multline", mathEnvWith para (Just "gathered") "multline") + , ("multline*", mathEnvWith para (Just "gathered") "multline*") + , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray") + , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*") + , ("align", mathEnvWith para (Just "aligned") "align") + , ("align*", mathEnvWith para (Just "aligned") "align*") + , ("alignat", mathEnvWith para (Just "aligned") "alignat") + , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") + , ("tikzpicture", rawVerbEnv "tikzpicture") ] figure :: PandocMonad m => LP m Blocks @@ -1287,19 +1307,32 @@ listenv name p = try $ do updateState $ \st -> st{ stateParserContext = oldCtx } return res -mathEnv :: PandocMonad m => (Inlines -> a) -> Maybe String -> String -> LP m a -mathEnv f innerEnv name = f <$> mathDisplay (inner <$> verbEnv name) +mathEnvWith :: PandocMonad m + => (Inlines -> a) -> Maybe String -> String -> LP m a +mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name) where inner x = case innerEnv of Nothing -> x Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++ "\\end{" ++ y ++ "}" +mathEnv :: PandocMonad m => String -> LP m String +mathEnv name = do + skipopts + optional blankline + let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) + charMuncher = skipMany comment *> + (many1 (noneOf "\\%") <|> try (string "\\%") + <|> try (string "\\\\") <|> count 1 anyChar) + res <- concat <$> manyTill charMuncher endEnv + return $ stripTrailingNewlines res + verbEnv :: PandocMonad m => String -> LP m String verbEnv name = do skipopts optional blankline let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) - res <- manyTill anyChar endEnv + charMuncher = anyChar + res <- manyTill charMuncher endEnv return $ stripTrailingNewlines res fancyverbEnv :: PandocMonad m => String -> LP m Blocks @@ -1314,7 +1347,7 @@ fancyverbEnv name = do codeBlockWith attr <$> verbEnv name orderedList' :: PandocMonad m => LP m Blocks -orderedList' = do +orderedList' = try $ do optional sp (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $ try $ char '[' *> anyOrderedListMarker <* char ']' @@ -1429,7 +1462,7 @@ complexNatbibCitation mode = try $ do -- tables -parseAligns :: PandocMonad m => LP m [(String, Alignment, String)] +parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))] parseAligns = try $ do bgroup let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced) @@ -1437,18 +1470,36 @@ parseAligns = try $ do let cAlign = AlignCenter <$ char 'c' let lAlign = AlignLeft <$ char 'l' let rAlign = AlignRight <$ char 'r' - let parAlign = AlignLeft <$ (char 'p' >> braced) + let parAlign = AlignLeft <$ char 'p' + -- algins from tabularx + let xAlign = AlignLeft <$ char 'X' + let mAlign = AlignLeft <$ char 'm' + let bAlign = AlignLeft <$ char 'b' let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign + <|> xAlign <|> mAlign <|> bAlign let alignPrefix = char '>' >> braced let alignSuffix = char '<' >> braced + let colWidth = try $ do + char '{' + ds <- many1 (oneOf "0123456789.") + spaces + string "\\linewidth" + char '}' + case safeRead ds of + Just w -> return w + Nothing -> return 0.0 let alignSpec = do spaces pref <- option "" alignPrefix spaces - ch <- alignChar + al <- alignChar + width <- colWidth <|> option 0.0 (do s <- braced + pos <- getPosition + report $ SkippedContent s pos + return 0.0) spaces suff <- option "" alignSuffix - return (pref, ch, suff) + return (al, width, (pref, suff)) aligns' <- sepEndBy alignSpec maybeBar spaces egroup @@ -1478,24 +1529,26 @@ amp :: PandocMonad m => LP m () amp = () <$ try (spaces' *> char '&' <* spaces') parseTableRow :: PandocMonad m - => Int -- ^ number of columns - -> [String] -- ^ prefixes - -> [String] -- ^ suffixes + => String -- ^ table environment name + -> [(String, String)] -- ^ pref/suffixes -> LP m [Blocks] -parseTableRow cols prefixes suffixes = try $ do - let tableCellRaw = many (notFollowedBy - (amp <|> lbreak <|> - (() <$ try (string "\\end"))) >> anyChar) - let minipage = try $ controlSeq "begin" *> string "{minipage}" *> - env "minipage" - (skipopts *> spaces' *> optional braced *> spaces' *> blocks) - let tableCell = minipage <|> - ((plain . trimInlines . mconcat) <$> many inline) +parseTableRow envname prefsufs = try $ do + let cols = length prefsufs + let tableCellRaw = concat <$> many + (do notFollowedBy amp + notFollowedBy lbreak + notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}")) + many1 (noneOf "&%\n\r\\") + <|> try (string "\\&") + <|> count 1 anyChar) + let plainify bs = case toList bs of + [Para ils] -> plain (fromList ils) + _ -> bs rawcells <- sepBy1 tableCellRaw amp guard $ length rawcells == cols - let rawcells' = zipWith3 (\c p s -> p ++ trim c ++ s) - rawcells prefixes suffixes - cells' <- mapM (parseFromString tableCell) rawcells' + let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs + let tableCell = plainify <$> blocks + cells' <- mapM (parseFromString' tableCell) rawcells' let numcells = length cells' guard $ numcells <= cols && numcells >= 1 guard $ cells' /= [mempty] @@ -1507,21 +1560,22 @@ parseTableRow cols prefixes suffixes = try $ do spaces' :: PandocMonad m => LP m () spaces' = spaces *> skipMany (comment *> spaces) -simpTable :: PandocMonad m => Bool -> LP m Blocks -simpTable hasWidthParameter = try $ do +simpTable :: PandocMonad m => String -> Bool -> LP m Blocks +simpTable envname hasWidthParameter = try $ do when hasWidthParameter $ () <$ (spaces' >> tok) skipopts - (prefixes, aligns, suffixes) <- unzip3 <$> parseAligns - let cols = length aligns + colspecs <- parseAligns + let (aligns, widths, prefsufs) = unzip3 colspecs + let cols = length colspecs optional $ controlSeq "caption" *> skipopts *> setCaption optional lbreak spaces' skipMany hline spaces' - header' <- option [] $ try (parseTableRow cols prefixes suffixes <* + header' <- option [] $ try (parseTableRow envname prefsufs <* lbreak <* many1 hline) spaces' - rows <- sepEndBy (parseTableRow cols prefixes suffixes) + rows <- sepEndBy (parseTableRow envname prefsufs) (lbreak <* optional (skipMany hline)) spaces' optional $ controlSeq "caption" *> skipopts *> setCaption @@ -1531,7 +1585,7 @@ simpTable hasWidthParameter = try $ do then replicate cols mempty else header' lookAhead $ controlSeq "end" -- make sure we're at end - return $ table mempty (zip aligns (repeat 0)) header'' rows + return $ table mempty (zip aligns widths) header'' rows removeDoubleQuotes :: String -> String removeDoubleQuotes ('"':xs) = diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5515c735b..e1c481311 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Markdown - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -55,11 +55,9 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) -import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) -import Text.Pandoc.Pretty (charWidth) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) @@ -72,10 +70,11 @@ type MarkdownParser m = ParserT [Char] ParserState m -- | Read markdown from an input string and return a Pandoc document. readMarkdown :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readMarkdown opts s = do - parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e @@ -155,9 +154,11 @@ litChar = escapedChar' inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines) inlinesInBalancedBrackets = do char '[' + pos <- getPosition (_, raw) <- withRaw $ charsInBalancedBrackets 1 guard $ not $ null raw - parseFromString (trimInlinesF . mconcat <$> many inline) (init raw) + parseFromString' (setPosition pos >> + trimInlinesF . mconcat <$> many inline) (init raw) charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m () charsInBalancedBrackets 0 = return () @@ -189,7 +190,7 @@ rawTitleBlockLine = do titleLine :: PandocMonad m => MarkdownParser m (F Inlines) titleLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString (many inline) raw + res <- parseFromString' (many inline) raw return $ trimInlinesF $ mconcat res authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines]) @@ -200,12 +201,12 @@ authorsLine = try $ do (trimInlinesF . mconcat <$> many (try $ notFollowedBy sep >> inline)) sep - sequence <$> parseFromString pAuthors raw + sequence <$> parseFromString' pAuthors raw dateLine :: PandocMonad m => MarkdownParser m (F Inlines) dateLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString (many inline) raw + res <- parseFromString' (many inline) raw return $ trimInlinesF $ mconcat res titleBlock :: PandocMonad m => MarkdownParser m () @@ -290,7 +291,7 @@ ignorable t = (T.pack "_") `T.isSuffixOf` t toMetaValue :: PandocMonad m => Text -> MarkdownParser m (F MetaValue) -toMetaValue x = toMeta <$> parseFromString parseBlocks (T.unpack x) +toMetaValue x = toMeta <$> parseFromString' parseBlocks (T.unpack x) where toMeta p = do p' <- p @@ -360,20 +361,20 @@ parseMarkdown = do optional titleBlock blocks <- parseBlocks st <- getState + -- check for notes with no corresponding note references + let notesUsed = stateNoteRefs st + let notesDefined = M.keys (stateNotes' st) + mapM_ (\n -> unless (n `Set.member` notesUsed) $ do + -- lookup to get sourcepos + case M.lookup n (stateNotes' st) of + Just (pos, _) -> report (NoteDefinedButNotUsed n pos) + Nothing -> error "The impossible happened.") notesDefined let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- stateMeta' st return $ Pandoc meta bs) st reportLogMessages (do guardEnabled Ext_east_asian_line_breaks - return $ bottomUp softBreakFilter doc) <|> return doc - -softBreakFilter :: [Inline] -> [Inline] -softBreakFilter (x:SoftBreak:y:zs) = - case (stringify x, stringify y) of - (xs@(_:_), (c:_)) - | charWidth (last xs) == 2 && charWidth c == 2 -> x:y:zs - _ -> x:SoftBreak:y:zs -softBreakFilter xs = xs + return $ eastAsianLineBreakFilter doc) <|> return doc referenceKey :: PandocMonad m => MarkdownParser m (F Blocks) referenceKey = try $ do @@ -392,7 +393,9 @@ referenceKey = try $ do src <- try betweenAngles <|> sourceURL tit <- option "" referenceTitle attr <- option nullAttr $ try $ - guardEnabled Ext_link_attributes >> skipSpaces >> attributes + do guardEnabled Ext_link_attributes + skipSpaces >> optional newline >> skipSpaces + attributes addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes >> many (try $ spnl >> keyValAttr) blanklines @@ -402,8 +405,12 @@ referenceKey = try $ do let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of - Just _ -> logMessage $ DuplicateLinkReference raw pos - Nothing -> return () + Just (t,a) | not (t == target && a == attr') -> + -- We don't warn on two duplicate keys if the targets are also + -- the same. This can happen naturally with --reference-location=block + -- or section. See #3701. + logMessage $ DuplicateLinkReference raw pos + _ -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty @@ -464,13 +471,12 @@ noteBlock = try $ do rest <- many $ try $ blanklines >> indentSpaces >> rawLines let raw = unlines (first:rest) ++ "\n" optional blanklines - parsed <- parseFromString parseBlocks raw - let newnote = (ref, parsed) + parsed <- parseFromString' parseBlocks raw oldnotes <- stateNotes' <$> getState - case lookup ref oldnotes of + case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () - updateState $ \s -> s { stateNotes' = newnote : oldnotes } + updateState $ \s -> s { stateNotes' = M.insert ref (pos, parsed) oldnotes } return mempty -- @@ -614,7 +620,7 @@ hrule = try $ do -- indentedLine :: PandocMonad m => MarkdownParser m String -indentedLine = indentSpaces >> anyLine >>= return . (++ "\n") +indentedLine = indentSpaces >> anyLineNewline blockDelimiter :: PandocMonad m => (Char -> Bool) @@ -772,7 +778,7 @@ blockQuote :: PandocMonad m => MarkdownParser m (F Blocks) blockQuote = do raw <- emailBlockQuote -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ (intercalate "\n" raw) ++ "\n\n" + contents <- parseFromString' parseBlocks $ (intercalate "\n" raw) ++ "\n\n" return $ B.blockQuote <$> contents -- @@ -868,8 +874,7 @@ listContinuationLine = try $ do notFollowedBy' listStart notFollowedByHtmlCloser optional indentSpaces - result <- anyLine - return $ result ++ "\n" + anyLineNewline listItem :: PandocMonad m => MarkdownParser m a @@ -885,7 +890,7 @@ listItem start = try $ do setState $ state {stateParserContext = ListItemState} -- parse the extracted block, which may contain various block elements: let raw = concat (first:continuations) - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw updateState (\st -> st {stateParserContext = oldContext}) return contents @@ -932,8 +937,8 @@ definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Bl definitionListItem compact = try $ do rawLine' <- anyLine raw <- many1 $ defRawBlock compact - term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawLine' - contents <- mapM (parseFromString parseBlocks . (++"\n")) raw + term <- parseFromString' (trimInlinesF . mconcat <$> many inline) rawLine' + contents <- mapM (parseFromString' parseBlocks . (++"\n")) raw optional blanklines return $ liftM2 (,) term (sequence contents) @@ -941,7 +946,7 @@ defRawBlock :: PandocMonad m => Bool -> MarkdownParser m String defRawBlock compact = try $ do hasBlank <- option False $ blankline >> return True defListMarker - firstline <- anyLine + firstline <- anyLineNewline let dline = try ( do notFollowedBy blankline notFollowedByHtmlCloser @@ -956,7 +961,7 @@ defRawBlock compact = try $ do ln <- indentSpaces >> notFollowedBy blankline >> anyLine lns <- many dline return $ trailing ++ unlines (ln:lns) - return $ trimr (firstline ++ "\n" ++ unlines rawlines ++ cont) ++ + return $ trimr (firstline ++ unlines rawlines ++ cont) ++ if hasBlank || not (null cont) then "\n\n" else "" definitionList :: PandocMonad m => MarkdownParser m (F Blocks) @@ -1088,13 +1093,19 @@ rawTeXBlock = do rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag + -- we don't want '<td> text' to be a code block: + skipMany spaceChar + indentlevel <- (blankline >> length <$> many (char ' ')) <|> return 0 -- try to find closing tag -- we set stateInHtmlBlock so that closing tags that can be either block or -- inline will not be parsed as inline tags oldInHtmlBlock <- stateInHtmlBlock <$> getState updateState $ \st -> st{ stateInHtmlBlock = Just tagtype } let closer = htmlTag (\x -> x ~== TagClose tagtype) - contents <- mconcat <$> many (notFollowedBy' closer >> block) + let block' = do notFollowedBy' closer + atMostSpaces indentlevel + block + contents <- mconcat <$> many block' result <- (closer >>= \(_, rawcloser) -> return ( return (B.rawBlock "html" $ stripMarkdownAttribute raw) <> @@ -1119,7 +1130,7 @@ lineBlock :: PandocMonad m => MarkdownParser m (F Blocks) lineBlock = try $ do guardEnabled Ext_line_blocks lines' <- lineBlockLines >>= - mapM (parseFromString (trimInlinesF . mconcat <$> many inline)) + mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) return $ B.lineBlock <$> sequence lines' -- @@ -1162,7 +1173,7 @@ simpleTableHeader headless = try $ do then replicate (length dashes) "" else rawHeads heads <- fmap sequence - $ mapM (parseFromString (mconcat <$> many plain)) + $ mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads' return (heads, aligns, indices) @@ -1208,7 +1219,7 @@ tableLine :: PandocMonad m => [Int] -> MarkdownParser m (F [Blocks]) tableLine indices = rawTableLine indices >>= - fmap sequence . mapM (parseFromString (mconcat <$> many plain)) + fmap sequence . mapM (parseFromString' (mconcat <$> many plain)) -- Parse a multiline table row and return a list of blocks (columns). multilineRow :: PandocMonad m @@ -1217,7 +1228,7 @@ multilineRow :: PandocMonad m multilineRow indices = do colLines <- many1 (rawTableLine indices) let cols = map unlines $ transpose colLines - fmap sequence $ mapM (parseFromString (mconcat <$> many plain)) cols + fmap sequence $ mapM (parseFromString' (mconcat <$> many plain)) cols -- Parses a table caption: inlines beginning with 'Table:' -- and followed by blank lines. @@ -1275,7 +1286,7 @@ multilineTableHeader headless = try $ do then replicate (length dashes) "" else map (unlines . map trim) rawHeadsList heads <- fmap sequence $ - mapM (parseFromString (mconcat <$> many plain)) $ + mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads return (heads, aligns, indices) @@ -1285,89 +1296,7 @@ multilineTableHeader headless = try $ do -- ending with a footer (dashed line followed by blank line). gridTable :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m ([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 :: PandocMonad m => Char -> ParserT [Char] st m ((Int, Int), Alignment) -gridPart ch = do - leftColon <- option False (True <$ char ':') - dashes <- many1 (char ch) - rightColon <- option False (True <$ char ':') - char '+' - let lengthDashes = length dashes + (if leftColon then 1 else 0) + - (if rightColon then 1 else 0) - let alignment = case (leftColon, rightColon) of - (True, True) -> AlignCenter - (True, False) -> AlignLeft - (False, True) -> AlignRight - (False, False) -> AlignDefault - return ((lengthDashes, lengthDashes + 1), alignment) - -gridDashedLines :: PandocMonad m => Char -> ParserT [Char] st m [((Int, Int), Alignment)] -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 :: PandocMonad m => Char -> MarkdownParser m Char -gridTableSep ch = try $ gridDashedLines ch >> return '\n' - --- | Parse header for a grid table. -gridTableHeader :: PandocMonad m => Bool -- ^ Headerless table - -> MarkdownParser m (F [Blocks], [Alignment], [Int]) -gridTableHeader headless = try $ do - optional blanklines - dashes <- gridDashedLines '-' - rawContent <- if headless - then return [] - else many1 (try (char '|' >> anyLine)) - underDashes <- if headless - then return dashes - else gridDashedLines '=' - guard $ length dashes == length underDashes - let lines' = map (snd . fst) underDashes - let indices = scanl (+) 0 lines' - let aligns = map snd underDashes - let rawHeads = if headless - then replicate (length underDashes) "" - else map (unlines . map trim) $ transpose - $ map (gridTableSplitLine indices) rawContent - heads <- fmap sequence $ mapM (parseFromString parseBlocks . trim) rawHeads - return (heads, aligns, indices) - -gridTableRawLine :: PandocMonad m => [Int] -> MarkdownParser m [String] -gridTableRawLine indices = do - char '|' - line <- anyLine - return (gridTableSplitLine indices line) - --- | Parse row of grid table. -gridTableRow :: PandocMonad m => [Int] - -> MarkdownParser m (F [Blocks]) -gridTableRow indices = do - colLines <- many1 (gridTableRawLine indices) - let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ - transpose colLines - fmap compactify <$> fmap sequence (mapM (parseFromString parseBlocks) 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 :: PandocMonad m => MarkdownParser m [Char] -gridTableFooter = blanklines +gridTable headless = gridTableWith' parseBlocks headless pipeBreak :: PandocMonad m => MarkdownParser m ([Alignment], [Int]) pipeBreak = try $ do @@ -1414,7 +1343,7 @@ pipeTableRow = try $ do let chunk = void (code <|> math <|> rawHtmlInline <|> escapedChar <|> rawLaTeXInline') <|> void (noneOf "|\n\r") let cellContents = ((trim . snd) <$> withRaw (many chunk)) >>= - parseFromString pipeTableCell + parseFromString' pipeTableCell cells <- cellContents `sepEndBy1` (char '|') -- surrounding pipes needed for a one-column table: guard $ not (length cells == 1 && not openPipe) @@ -1522,6 +1451,7 @@ inline = choice [ whitespace , autoLink , spanHtml , rawHtmlInline + , escapedNewline , escapedChar , rawLaTeXInline' , exampleRef @@ -1538,16 +1468,20 @@ escapedChar' = try $ do (guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum)) <|> (guardEnabled Ext_angle_brackets_escapable >> oneOf "\\`*_{}[]()>#+-.!~\"<>") - <|> (guardEnabled Ext_escaped_line_breaks >> char '\n') <|> oneOf "\\`*_{}[]()>#+-.!~\"" +escapedNewline :: PandocMonad m => MarkdownParser m (F Inlines) +escapedNewline = try $ do + guardEnabled Ext_escaped_line_breaks + char '\\' + lookAhead (char '\n') -- don't consume the newline (see #3730) + return $ return B.linebreak + escapedChar :: PandocMonad m => MarkdownParser m (F Inlines) escapedChar = do result <- escapedChar' case result of ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space - '\n' -> guardEnabled Ext_escaped_line_breaks >> - return (return B.linebreak) -- "\[newline]" is a linebreak _ -> return $ return $ B.str [result] ltSign :: PandocMonad m => MarkdownParser m (F Inlines) @@ -1629,9 +1563,9 @@ ender c n = try $ do three :: PandocMonad m => Char -> MarkdownParser m (F Inlines) three c = do contents <- mconcat <$> many (notFollowedBy (ender c 1) >> inline) - (ender c 3 >> return ((B.strong . B.emph) <$> contents)) - <|> (ender c 2 >> one c (B.strong <$> contents)) - <|> (ender c 1 >> two c (B.emph <$> contents)) + (ender c 3 >> updateLastStrPos >> return ((B.strong . B.emph) <$> contents)) + <|> (ender c 2 >> updateLastStrPos >> one c (B.strong <$> contents)) + <|> (ender c 1 >> updateLastStrPos >> two c (B.emph <$> contents)) <|> return (return (B.str [c,c,c]) <> contents) -- Parse inlines til you hit two c's, and emit strong. @@ -1639,7 +1573,8 @@ three c = do two :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines) two c prefix' = do contents <- mconcat <$> many (try $ notFollowedBy (ender c 2) >> inline) - (ender c 2 >> return (B.strong <$> (prefix' <> contents))) + (ender c 2 >> updateLastStrPos >> + return (B.strong <$> (prefix' <> contents))) <|> return (return (B.str [c,c]) <> (prefix' <> contents)) -- Parse inlines til you hit a c, and emit emph. @@ -1650,7 +1585,7 @@ one c prefix' = do <|> try (string [c,c] >> notFollowedBy (ender c 1) >> two c mempty) ) - (ender c 1 >> return (B.emph <$> (prefix' <> contents))) + (ender c 1 >> updateLastStrPos >> return (B.emph <$> (prefix' <> contents))) <|> return (return (B.str [c]) <> (prefix' <> contents)) strongOrEmph :: PandocMonad m => MarkdownParser m (F Inlines) @@ -1814,15 +1749,17 @@ referenceLink :: PandocMonad m referenceLink constructor (lab, raw) = do sp <- (True <$ lookAhead (char ' ')) <|> return False (_,raw') <- option (mempty, "") $ - lookAhead (try (guardEnabled Ext_citations >> - spnl >> normalCite >> return (mempty, ""))) + lookAhead (try (do guardEnabled Ext_citations + guardDisabled Ext_spaced_reference_links <|> spnl + normalCite + return (mempty, ""))) <|> - try (spnl >> reference) + try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference) when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' - parsedRaw <- parseFromString (mconcat <$> many inline) raw' - fallback <- parseFromString (mconcat <$> many inline) $ dropBrackets raw + parsedRaw <- parseFromString' (mconcat <$> many inline) raw' + fallback <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw implicitHeaderRefs <- option False $ True <$ guardEnabled Ext_implicit_header_references let makeFallback = do @@ -1887,16 +1824,17 @@ note :: PandocMonad m => MarkdownParser m (F Inlines) note = try $ do guardEnabled Ext_footnotes ref <- noteMarker + updateState $ \st -> st{ stateNoteRefs = Set.insert ref (stateNoteRefs st) } return $ do notes <- asksF stateNotes' - case lookup ref notes of + case M.lookup ref notes of Nothing -> return $ B.str $ "[^" ++ ref ++ "]" - Just contents -> do + Just (_pos, contents) -> do st <- askF -- process the note in a context that doesn't resolve -- notes, to avoid infinite looping with notes inside -- notes: - let contents' = runF contents st{ stateNotes' = [] } + let contents' = runF contents st{ stateNotes' = M.empty } return $ B.note contents' inlineNote :: PandocMonad m => MarkdownParser m (F Inlines) @@ -2028,7 +1966,7 @@ textualCite = try $ do let (spaces',raw') = span isSpace raw spc | null spaces' = mempty | otherwise = B.space - lab <- parseFromString (mconcat <$> many inline) $ dropBrackets raw' + lab <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw' fallback <- referenceLink B.linkWith (lab,raw') return $ do fallback' <- fallback diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index b35f39aad..a3ff60c14 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -3,7 +3,7 @@ {-# LANGUAGE TypeSynonymInstances #-} -- RelaxedPolyRec needed for inlinesBetween on GHC < 7 {- - Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2012-2017 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.MediaWiki - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -41,6 +41,7 @@ module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isDigit, isSpace) +import Data.Text (Text, unpack) import qualified Data.Foldable as F import Data.List (intercalate, intersperse, isPrefixOf) import qualified Data.Map as M @@ -64,7 +65,7 @@ import Text.Pandoc.XML (fromEntities) -- | Read mediawiki from an input string and return a Pandoc document. readMediaWiki :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readMediaWiki opts s = do parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts @@ -74,8 +75,9 @@ readMediaWiki opts s = do , mwHeaderMap = M.empty , mwIdentifierList = Set.empty , mwLogMessages = [] + , mwInTT = False } - (s ++ "\n") + (unpack s ++ "\n") case parsed of Right result -> return result Left e -> throwError e @@ -87,6 +89,7 @@ data MWState = MWState { mwOptions :: ReaderOptions , mwHeaderMap :: M.Map Inlines String , mwIdentifierList :: Set.Set String , mwLogMessages :: [LogMessage] + , mwInTT :: Bool } type MWParser m = ParserT [Char] MWState m @@ -569,7 +572,12 @@ inlineTag = do TagOpen "sub" _ -> B.subscript <$> inlinesInTags "sub" TagOpen "sup" _ -> B.superscript <$> inlinesInTags "sup" TagOpen "code" _ -> encode <$> inlinesInTags "code" - TagOpen "tt" _ -> encode <$> inlinesInTags "tt" + TagOpen "tt" _ -> do + inTT <- mwInTT <$> getState + updateState $ \st -> st{ mwInTT = True } + result <- encode <$> inlinesInTags "tt" + updateState $ \st -> st{ mwInTT = inTT } + return result TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> charsInTags "hask" _ -> B.rawInline "html" . snd <$> htmlTag (~== tag) @@ -688,6 +696,10 @@ strong = B.strong <$> nested (inlinesBetween start end) end = try $ sym "'''" doubleQuotes :: PandocMonad m => MWParser m Inlines -doubleQuotes = B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) +doubleQuotes = do + guardEnabled Ext_smart + inTT <- mwInTT <$> getState + guard (not inTT) + B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar closeDoubleQuote = try $ sym "\"" diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 2e307fa4f..abc2ed38a 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Native - Copyright : Copyright (C) 2011-2015 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -37,6 +37,7 @@ import Text.Pandoc.Shared (safeRead) import Control.Monad.Except (throwError) import Text.Pandoc.Class import Text.Pandoc.Error +import Data.Text (Text, unpack) -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, @@ -50,22 +51,22 @@ import Text.Pandoc.Error -- readNative :: PandocMonad m => ReaderOptions - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readNative _ s = - case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of + case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead (unpack s)) of Right doc -> return doc Left _ -> throwError $ PandocParseError "couldn't read native" -readBlocks :: String -> Either PandocError [Block] -readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s) +readBlocks :: Text -> Either PandocError [Block] +readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead (unpack s)) -readBlock :: String -> Either PandocError Block -readBlock s = maybe (Plain <$> readInlines s) Right (safeRead s) +readBlock :: Text -> Either PandocError Block +readBlock s = maybe (Plain <$> readInlines s) Right (safeRead (unpack s)) -readInlines :: String -> Either PandocError [Inline] -readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead s) +readInlines :: Text -> Either PandocError [Inline] +readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead (unpack s)) -readInline :: String -> Either PandocError Inline -readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ s) Right (safeRead s) +readInline :: Text -> Either PandocError Inline +readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ unpack s) Right (safeRead (unpack s)) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index cf1c8f479..591d7590e 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -2,6 +2,7 @@ module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State import Data.Char (toUpper) +import Data.Text (Text, unpack, pack) import Data.Default import Data.Generics import Text.HTML.TagSoup.Entity (lookupEntity) @@ -28,9 +29,10 @@ instance Default OPMLState where , opmlDocDate = mempty } -readOPML :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readOPML _ inp = do - (bs, st') <- flip runStateT def (mapM parseBlock $ normalizeTree $ parseXML inp) + (bs, st') <- flip runStateT def + (mapM parseBlock $ normalizeTree $ parseXML (unpack inp)) return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ @@ -69,10 +71,10 @@ asHtml :: PandocMonad m => String -> OPML m Inlines asHtml s = (\(Pandoc _ bs) -> case bs of [Plain ils] -> fromList ils - _ -> mempty) <$> (lift $ readHtml def s) + _ -> mempty) <$> (lift $ readHtml def (pack s)) asMarkdown :: PandocMonad m => String -> OPML m Blocks -asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def s) +asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def (pack s)) getBlocks :: PandocMonad m => Element -> OPML m Blocks getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs index b056f1ecc..3d716ba19 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs @@ -59,10 +59,6 @@ withState :: (state -> a -> (state, b)) -> ArrowState state a b withState = ArrowState . uncurry -- | Constructor -withState' :: ((state, a) -> (state, b)) -> ArrowState state a b -withState' = ArrowState - --- | Constructor modifyState :: (state -> state ) -> ArrowState state a a modifyState = ArrowState . first @@ -79,10 +75,6 @@ extractFromState :: (state -> b ) -> ArrowState state x b extractFromState f = ArrowState $ \(state,_) -> (state, f state) -- | Constructor -withUnchangedState :: (state -> a -> b ) -> ArrowState state a b -withUnchangedState f = ArrowState $ \(state,a) -> (state, f state a) - --- | Constructor tryModifyState :: (state -> Either f state) -> ArrowState state a (Either f a) tryModifyState f = ArrowState $ \(state,a) @@ -107,43 +99,9 @@ instance ArrowChoice (ArrowState state) where Left l -> (s, Left l) Right r -> second Right $ runArrowState a (s,r) -instance ArrowLoop (ArrowState state) where - loop a = ArrowState $ \(s, x) - -> let (s', (x', _d)) = runArrowState a (s, (x, _d)) - in (s', x') - instance ArrowApply (ArrowState state) where app = ArrowState $ \(s, (f,b)) -> runArrowState f (s,b) - --- | Embedding of a state arrow in a state arrow with a different state type. -switchState :: (s -> s') -> (s' -> s) -> ArrowState s' x y -> ArrowState s x y -switchState there back a = ArrowState $ first there - >>> runArrowState a - >>> first back - --- | Lift a state arrow to modify the state of an arrow --- with a different state type. -liftToState :: (s -> s') -> ArrowState s' s s -> ArrowState s x x -liftToState unlift a = modifyState $ unlift &&& id - >>> runArrowState a - >>> snd - --- | Switches the type of the state temporarily. --- Drops the intermediate result state, behaving like the identity arrow, --- save for side effects in the state. -withSubState :: ArrowState s x s2 -> ArrowState s2 s s -> ArrowState s x x -withSubState unlift a = keepingTheValue (withSubState unlift a) >>^ fst - --- | Switches the type of the state temporarily. --- Returns the resulting sub-state. -withSubState' :: ArrowState s x s' -> ArrowState s' s s -> ArrowState s x s' -withSubState' unlift a = ArrowState $ runArrowState unlift - >>> switch - >>> runArrowState a - >>> switch - where switch (x,y) = (y,x) - -- | Switches the type of the state temporarily. -- Drops the intermediate result state, behaving like a fallible -- identity arrow, save for side effects in the state. @@ -175,42 +133,6 @@ foldS :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m foldS a = ArrowState $ \(s,f) -> foldr a' (s,mempty) f where a' x (s',m) = second (m <>) $ runArrowState a (s',x) --- | Fold a state arrow through something 'Foldable'. Collect the results --- in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. -foldSL :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m -foldSL a = ArrowState $ \(s,f) -> foldl a' (s,mempty) f - where a' (s',m) x = second (m <>) $ runArrowState a (s',x) - --- | Fold a fallible state arrow through something 'Foldable'. Collect the --- results in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. --- If the iteration fails, the state will be reset to the initial one. -foldS' :: (Foldable f, Monoid m) - => ArrowState s x (Either e m) - -> ArrowState s (f x) (Either e m) -foldS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mempty) f - where a' s x (s',Right m) = case runArrowState a (s',x) of - (s'',Right m') -> (s'', Right (m <> m')) - (_ ,Left e ) -> (s , Left e) - a' _ _ e = e - --- | Fold a fallible state arrow through something 'Foldable'. Collect the --- results in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. --- If the iteration fails, the state will be reset to the initial one. -foldSL' :: (Foldable f, Monoid m) - => ArrowState s x (Either e m) - -> ArrowState s (f x) (Either e m) -foldSL' a = ArrowState $ \(s,f) -> foldl (a' s) (s,Right mempty) f - where a' s (s',Right m) x = case runArrowState a (s',x) of - (s'',Right m') -> (s'', Right (m <> m')) - (_ ,Left e ) -> (s , Left e) - a' _ e _ = e - -- | Fold a state arrow through something 'Foldable'. Collect the results in a -- 'MonadPlus'. iterateS :: (Foldable f, MonadPlus m) @@ -239,15 +161,3 @@ iterateS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mzero) f (s'',Right m') -> (s'',Right $ mplus m $ return m') (_ ,Left e ) -> (s ,Left e ) a' _ _ e = e - --- | Fold a fallible state arrow through something 'Foldable'. --- Collect the results in a 'MonadPlus'. --- If the iteration fails, the state will be reset to the initial one. -iterateSL' :: (Foldable f, MonadPlus m) - => ArrowState s x (Either e y ) - -> ArrowState s (f x) (Either e (m y)) -iterateSL' a = ArrowState $ \(s,f) -> foldl (a' s) (s,Right mzero) f - where a' s (s',Right m) x = case runArrowState a (s',x) of - (s'',Right m') -> (s'',Right $ mplus m $ return m') - (_ ,Left e ) -> (s ,Left e ) - a' _ e _ = e diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs index 218a85661..ecef8b6e3 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs @@ -40,10 +40,7 @@ with an equivalent return value. module Text.Pandoc.Readers.Odt.Arrows.Utils where import Control.Arrow -import Control.Monad ( join, MonadPlus(..) ) - -import qualified Data.Foldable as F -import Data.Monoid +import Control.Monad ( join ) import Text.Pandoc.Readers.Odt.Generic.Fallible import Text.Pandoc.Readers.Odt.Generic.Utils @@ -63,12 +60,6 @@ and5 :: (Arrow a) and6 :: (Arrow a) => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5 -> a b (c0,c1,c2,c3,c4,c5 ) -and7 :: (Arrow a) - => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5->a b c6 - -> a b (c0,c1,c2,c3,c4,c5,c6 ) -and8 :: (Arrow a) - => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5->a b c6->a b c7 - -> a b (c0,c1,c2,c3,c4,c5,c6,c7) and3 a b c = (and2 a b ) &&& c >>^ \((z,y ) , x) -> (z,y,x ) @@ -78,10 +69,6 @@ and5 a b c d e = (and4 a b c d ) &&& e >>^ \((z,y,x,w ) , v) -> (z,y,x,w,v ) and6 a b c d e f = (and5 a b c d e ) &&& f >>^ \((z,y,x,w,v ) , u) -> (z,y,x,w,v,u ) -and7 a b c d e f g = (and6 a b c d e f ) &&& g - >>^ \((z,y,x,w,v,u ) , t) -> (z,y,x,w,v,u,t ) -and8 a b c d e f g h = (and7 a b c d e f g) &&& h - >>^ \((z,y,x,w,v,u,t) , s) -> (z,y,x,w,v,u,t,s) liftA2 :: (Arrow a) => (x -> y -> z) -> a b x -> a b y -> a b z liftA2 f a b = a &&& b >>^ uncurry f @@ -98,19 +85,11 @@ liftA5 :: (Arrow a) => (z->y->x->w->v -> r) liftA6 :: (Arrow a) => (z->y->x->w->v->u -> r) -> a b z->a b y->a b x->a b w->a b v->a b u -> a b r -liftA7 :: (Arrow a) => (z->y->x->w->v->u->t -> r) - -> a b z->a b y->a b x->a b w->a b v->a b u->a b t - -> a b r -liftA8 :: (Arrow a) => (z->y->x->w->v->u->t->s -> r) - -> a b z->a b y->a b x->a b w->a b v->a b u->a b t->a b s - -> a b r liftA3 fun a b c = and3 a b c >>^ uncurry3 fun liftA4 fun a b c d = and4 a b c d >>^ uncurry4 fun liftA5 fun a b c d e = and5 a b c d e >>^ uncurry5 fun liftA6 fun a b c d e f = and6 a b c d e f >>^ uncurry6 fun -liftA7 fun a b c d e f g = and7 a b c d e f g >>^ uncurry7 fun -liftA8 fun a b c d e f g h = and8 a b c d e f g h >>^ uncurry8 fun liftA :: (Arrow a) => (y -> z) -> a b y -> a b z liftA fun a = a >>^ fun @@ -124,28 +103,12 @@ liftA fun a = a >>^ fun duplicate :: (Arrow a) => a b (b,b) duplicate = arr $ join (,) --- | Lifts the combination of two values into an arrow. -joinOn :: (Arrow a) => (x -> y -> z) -> a (x,y) z -joinOn = arr.uncurry - -- | Applies a function to the uncurried result-pair of an arrow-application. -- (The %-symbol was chosen to evoke an association with pairs.) (>>%) :: (Arrow a) => a x (b,c) -> (b -> c -> d) -> a x d a >>% f = a >>^ uncurry f --- | '(>>%)' with its arguments flipped -(%<<) :: (Arrow a) => (b -> c -> d) -> a x (b,c) -> a x d -(%<<) = flip (>>%) - --- | Precomposition with an uncurried function -(%>>) :: (Arrow a) => (b -> c -> d) -> a d r -> a (b,c) r -f %>> a = uncurry f ^>> a - --- | Precomposition with an uncurried function (right to left variant) -(<<%) :: (Arrow a) => a d r -> (b -> c -> d) -> a (b,c) r -(<<%) = flip (%>>) - -infixr 2 >>%, %<<, %>>, <<% +infixr 2 >>% -- | Duplicate a value and apply an arrow to the second instance. @@ -156,56 +119,6 @@ infixr 2 >>%, %<<, %>>, <<% keepingTheValue :: (Arrow a) => a b c -> a b (b,c) keepingTheValue a = returnA &&& a --- | Duplicate a value and apply an arrow to the first instance. --- Aequivalent to --- > \a -> duplicate >>> first a --- or --- > \a -> a &&& returnA -keepingTheValue' :: (Arrow a) => a b c -> a b (c,b) -keepingTheValue' a = a &&& returnA - --- | 'bind' from the "Maybe"-Monad lifted into an 'ArrowChoice'. --- Actually, it's the more complex '(>=>)', because 'bind' alone does not --- combine as nicely in arrow form. --- The current implementation is not the most efficient one, because it can --- not return directly if a 'Nothing' is encountered. That in turn follows --- from the type system, as 'Nothing' has an "invisible" type parameter that --- can not be dropped early. --- --- Also, there probably is a way to generalize this to other monads --- or applicatives, but I'm leaving that as an exercise to the reader. --- I have a feeling there is a new Arrow-typeclass to be found that is less --- restrictive than 'ArrowApply'. If it is already out there, --- I have not seen it yet. ('ArrowPlus' for example is not general enough.) -(>>>=) :: (ArrowChoice a) => a x (Maybe b) -> a b (Maybe c) -> a x (Maybe c) -a1 >>>= a2 = a1 >>> maybeToChoice >>> right a2 >>> choiceToMaybe >>^ join - -infixr 2 >>>= - --- | 'mplus' Lifted into an arrow. No 'ArrowPlus' required. --- (But still different from a true bind) -(>++<) :: (Arrow a, MonadPlus m) => a x (m b) -> a x (m b) -> a x (m b) -(>++<) = liftA2 mplus - --- | Left-compose with a pure function -leftLift :: (ArrowChoice a) => (l -> l') -> a (Either l r) (Either l' r) -leftLift = left.arr - --- | Right-compose with a pure function -rightLift :: (ArrowChoice a) => (r -> r') -> a (Either l r) (Either l r') -rightLift = right.arr - - -( ^+++ ) :: (ArrowChoice a) => (b -> c) -> a b' c' -> a (Either b b') (Either c c') -( +++^ ) :: (ArrowChoice a) => a b c -> (b' -> c') -> a (Either b b') (Either c c') -( ^+++^ ) :: (ArrowChoice a) => (b -> c) -> (b' -> c') -> a (Either b b') (Either c c') - -l ^+++ r = leftLift l >>> right r -l +++^ r = left l >>> rightLift r -l ^+++^ r = leftLift l >>> rightLift r - -infixr 2 ^+++, +++^, ^+++^ - ( ^||| ) :: (ArrowChoice a) => (b -> d) -> a c d -> a (Either b c) d ( |||^ ) :: (ArrowChoice a) => a b d -> (c -> d) -> a (Either b c) d ( ^|||^ ) :: (ArrowChoice a) => (b -> d) -> (c -> d) -> a (Either b c) d @@ -218,33 +131,12 @@ infixr 2 ^||| , |||^, ^|||^ ( ^&&& ) :: (Arrow a) => (b -> c) -> a b c' -> a b (c,c') ( &&&^ ) :: (Arrow a) => a b c -> (b -> c') -> a b (c,c') -( ^&&&^ ) :: (Arrow a) => (b -> c) -> (b -> c') -> a b (c,c') l ^&&& r = arr l &&& r l &&&^ r = l &&& arr r -l ^&&&^ r = arr l &&& arr r - -infixr 3 ^&&&, &&&^, ^&&&^ -( ^*** ) :: (Arrow a) => (b -> c) -> a b' c' -> a (b,b') (c,c') -( ***^ ) :: (Arrow a) => a b c -> (b' -> c') -> a (b,b') (c,c') -( ^***^ ) :: (Arrow a) => (b -> c) -> (b' -> c') -> a (b,b') (c,c') +infixr 3 ^&&&, &&&^ -l ^*** r = arr l *** r -l ***^ r = l *** arr r -l ^***^ r = arr l *** arr r - -infixr 3 ^***, ***^, ^***^ - --- | A version of --- --- >>> \p -> arr (\x -> if p x the Right x else Left x) --- --- but with p being an arrow -choose :: (ArrowChoice a) => a b Bool -> a b (Either b b) -choose checkValue = keepingTheValue checkValue >>^ select - where select (x,True ) = Right x - select (x,False ) = Left x -- | Converts @Right a@ into @Just a@ and @Left _@ into @Nothing@. choiceToMaybe :: (ArrowChoice a) => a (Either l r) (Maybe r) @@ -258,130 +150,15 @@ maybeToChoice = arr maybeToEither returnV :: (Arrow a) => c -> a x c returnV = arr.const --- | 'returnA' dropping everything -returnA_ :: (Arrow a) => a _b () -returnA_ = returnV () - --- | Wrapper for an arrow that can be evaluated im parallel. All --- Arrows can be evaluated in parallel, as long as they return a --- monoid. -newtype ParallelArrow a b c = CoEval { evalParallelArrow :: a b c } - deriving (Eq, Ord, Show) - -instance (Arrow a, Monoid m) => Monoid (ParallelArrow a b m) where - mempty = CoEval $ returnV mempty - (CoEval a) `mappend` (CoEval ~b) = CoEval $ a &&& b >>% mappend - --- | Evaluates a collection of arrows in a parallel fashion. --- --- This is in essence a fold of '(&&&)' over the collection, --- so the actual execution order and parallelity depends on the --- implementation of '(&&&)' in the arrow in question. --- The default implementation of '(&&&)' for example keeps the --- order as given in the collection. --- --- This function can be seen as a generalization of --- 'Control.Applicative.sequenceA' to arrows or as an alternative to --- a fold with 'Control.Applicative.WrappedArrow', which --- substitutes the monoid with function application. --- -coEval :: (Arrow a, F.Foldable f, Monoid m) => f (a b m) -> a b m -coEval = evalParallelArrow . (F.foldMap CoEval) - -- | Defines Left as failure, Right as success type FallibleArrow a input failure success = a input (Either failure success) -type ReFallibleArrow a failure success success' - = FallibleArrow a (Either failure success) failure success' - --- | Wrapper for fallible arrows. Fallible arrows are all arrows that return --- an Either value where left is a faliure and right is a success value. -newtype AlternativeArrow a input failure success - = TryArrow { evalAlternativeArrow :: FallibleArrow a input failure success } - - -instance (ArrowChoice a, Monoid failure) - => Monoid (AlternativeArrow a input failure success) where - mempty = TryArrow $ returnV $ Left mempty - (TryArrow a) `mappend` (TryArrow b) - = TryArrow $ a &&& b - >>^ \(a',~b') - -> ( (\a'' -> left (mappend a'') b') ||| Right ) - a' - --- | Evaluates a collection of fallible arrows, trying each one in succession. --- Left values are interpreted as failures, right values as successes. --- --- The evaluation is stopped once an arrow succeeds. --- Up to that point, all failures are collected in the failure-monoid. --- Note that '()' is a monoid, and thus can serve as a failure-collector if --- you are uninterested in the exact failures. --- --- This is in essence a fold of '(&&&)' over the collection, enhanced with a --- little bit of repackaging, so the actual execution order depends on the --- implementation of '(&&&)' in the arrow in question. --- The default implementation of '(&&&)' for example keeps the --- order as given in the collection. --- -tryArrows :: (ArrowChoice a, F.Foldable f, Monoid failure) - => f (FallibleArrow a b failure success) - -> FallibleArrow a b failure success -tryArrows = evalAlternativeArrow . (F.foldMap TryArrow) - --- -liftSuccess :: (ArrowChoice a) - => (success -> success') - -> ReFallibleArrow a failure success success' -liftSuccess = rightLift - -- liftAsSuccess :: (ArrowChoice a) => a x success -> FallibleArrow a x failure success liftAsSuccess a = a >>^ Right --- -asFallibleArrow :: (ArrowChoice a) - => a x success - -> FallibleArrow a x failure success -asFallibleArrow a = a >>^ Right - --- | Raises an error into a 'ReFallibleArrow' if the arrow is already in --- "error mode" -liftError :: (ArrowChoice a, Monoid failure) - => failure - -> ReFallibleArrow a failure success success -liftError e = leftLift (e <>) - --- | Raises an error into a 'FallibleArrow', droping both the arrow input --- and any previously stored error value. -_raiseA :: (ArrowChoice a) - => failure - -> FallibleArrow a x failure success -_raiseA e = returnV (Left e) - --- | Raises an empty error into a 'FallibleArrow', droping both the arrow input --- and any previously stored error value. -_raiseAEmpty :: (ArrowChoice a, Monoid failure) - => FallibleArrow a x failure success -_raiseAEmpty = _raiseA mempty - --- | Raises an error into a 'ReFallibleArrow', possibly appending the new error --- to an existing one -raiseA :: (ArrowChoice a, Monoid failure) - => failure - -> ReFallibleArrow a failure success success -raiseA e = arr $ Left.(either (<> e) (const e)) - --- | Raises an empty error into a 'ReFallibleArrow'. If there already is an --- error, nothing changes. --- (Note that this function is only aequivalent to @raiseA mempty@ iff the --- failure monoid follows the monoid laws.) -raiseAEmpty :: (ArrowChoice a, Monoid failure) - => ReFallibleArrow a failure success success -raiseAEmpty = arr (fromRight (const mempty) >>> Left) - - -- | Execute the second arrow if the first succeeds (>>?) :: (ArrowChoice a) => FallibleArrow a x failure success @@ -410,20 +187,6 @@ a >>?^? b = a >>> Left ^|||^ b -> FallibleArrow a x failure success' a ^>>? b = a ^>> Left ^||| b --- | Execute the lifted second arrow if the lifted first arrow succeeds -(^>>?^) :: (ArrowChoice a) - => (x -> Either failure success) - -> (success -> success') - -> FallibleArrow a x failure success' -a ^>>?^ f = arr $ a >>> right f - --- | Execute the lifted second arrow if the lifted first arrow succeeds -(^>>?^?) :: (ArrowChoice a) - => (x -> Either failure success) - -> (success -> Either failure success') - -> FallibleArrow a x failure success' -a ^>>?^? f = a ^>> Left ^|||^ f - -- | Execute the second, non-fallible arrow if the first arrow succeeds (>>?!) :: (ArrowChoice a) => FallibleArrow a x failure success @@ -453,33 +216,9 @@ a ^>>?% f = arr a >>?^ (uncurry f) a >>?%? f = a >>?^? (uncurry f) infixr 1 >>?, >>?^, >>?^? -infixr 1 ^>>?, ^>>?^, ^>>?^?, >>?! +infixr 1 ^>>?, >>?! infixr 1 >>?%, ^>>?%, >>?%? --- | Keep values that are Right, replace Left values by a constant. -ifFailedUse :: (ArrowChoice a) => v -> a (Either f v) v -ifFailedUse v = arr $ either (const v) id - --- | '(&&)' lifted into an arrow -(<&&>) :: (Arrow a) => a x Bool -> a x Bool -> a x Bool -(<&&>) = liftA2 (&&) - --- | '(||)' lifted into an arrow -(<||>) :: (Arrow a) => a x Bool -> a x Bool -> a x Bool -(<||>) = liftA2 (||) - --- | An equivalent of '(&&)' in a fallible arrow -(>&&<) :: (ArrowChoice a, Monoid f) => FallibleArrow a x f s - -> FallibleArrow a x f s' - -> FallibleArrow a x f (s,s') -(>&&<) = liftA2 chooseMin - --- | An equivalent of '(||)' in some forms of fallible arrows -(>||<) :: (ArrowChoice a, Monoid f, Monoid s) => FallibleArrow a x f s - -> FallibleArrow a x f s - -> FallibleArrow a x f s -(>||<) = liftA2 chooseMax - -- | An arrow version of a short-circuit (<|>) ifFailedDo :: (ArrowChoice a) => FallibleArrow a x f y @@ -489,7 +228,4 @@ ifFailedDo a b = keepingTheValue a >>> repackage ^>> (b |||^ Right) where repackage (x , Left _) = Left x repackage (_ , Right y) = Right y -infixr 4 <&&>, <||>, >&&<, >||< infixr 1 `ifFailedDo` - - diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index a1bd8cb59..777c10df5 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -94,8 +94,6 @@ data ReaderState , envMedia :: Media -- | Hold binary resources used in the document , odtMediaBag :: MediaBag --- , sequences --- , trackedChangeIDs } deriving ( Show ) @@ -899,9 +897,6 @@ read_reference_ref = matchingElement NsText "reference-ref" -- Entry point ---------------------- ---read_plain_content :: OdtReaderSafe _x Inlines ---read_plain_content = strContent >>^ text - read_text :: OdtReaderSafe _x Pandoc read_text = matchChildContent' [ read_header , read_paragraph diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs index 877443543..4d6a67b8e 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs @@ -39,10 +39,6 @@ compatible instances of "ArrowChoice". -- We export everything module Text.Pandoc.Readers.Odt.Generic.Fallible where -import Control.Applicative -import Control.Monad - -import qualified Data.Foldable as F import Data.Monoid ((<>)) -- | Default for now. Will probably become a class at some point. @@ -51,16 +47,6 @@ type Failure = () type Fallible a = Either Failure a --- | False -> Left (), True -> Right () -boolToEither :: Bool -> Fallible () -boolToEither False = Left () -boolToEither True = Right () - --- | False -> Left (), True -> Right () -boolToChoice :: Bool -> Fallible () -boolToChoice False = Left () -boolToChoice True = Right () - -- maybeToEither :: Maybe a -> Fallible a maybeToEither (Just a) = Right a @@ -71,21 +57,11 @@ eitherToMaybe :: Either _l a -> Maybe a eitherToMaybe (Left _) = Nothing eitherToMaybe (Right a) = Just a --- | > untagEither === either id id -untagEither :: Either a a -> a -untagEither (Left a) = a -untagEither (Right a) = a - -- | > fromLeft f === either f id fromLeft :: (a -> b) -> Either a b -> b fromLeft f (Left a) = f a fromLeft _ (Right b) = b --- | > fromRight f === either id f -fromRight :: (a -> b) -> Either b a -> b -fromRight _ (Left b) = b -fromRight f (Right a) = f a - -- | > recover a === fromLeft (const a) === either (const a) id recover :: a -> Either _f a -> a recover a (Left _) = a @@ -110,24 +86,6 @@ collapseEither (Left f ) = Left f collapseEither (Right (Left f)) = Left f collapseEither (Right (Right x)) = Right x --- | If either of the values represents an error, the result is a --- (possibly combined) error. If both values represent a success, --- both are returned. -chooseMin :: (Monoid a) => Either a b -> Either a b' -> Either a (b,b') -chooseMin = chooseMinWith (,) - --- | If either of the values represents an error, the result is a --- (possibly combined) error. If both values represent a success, --- a combination is returned. -chooseMinWith :: (Monoid a) => (b -> b' -> c) - -> Either a b - -> Either a b' - -> Either a c -chooseMinWith (><) (Right a) (Right b) = Right $ a >< b -chooseMinWith _ (Left a) (Left b) = Left $ a <> b -chooseMinWith _ (Left a) _ = Left a -chooseMinWith _ _ (Left b) = Left b - -- | If either of the values represents a non-error, the result is a -- (possibly combined) non-error. If both values represent an error, an error -- is returned. @@ -152,87 +110,11 @@ chooseMaxWith _ _ (Right b) = Right b class ChoiceVector v where spreadChoice :: v (Either f a) -> Either f (v a) --- Let's do a few examples first - -instance ChoiceVector Maybe where - spreadChoice (Just (Left f)) = Left f - spreadChoice (Just (Right x)) = Right (Just x) - spreadChoice Nothing = Right Nothing - -instance ChoiceVector (Either l) where - spreadChoice (Right (Left f)) = Left f - spreadChoice (Right (Right x)) = Right (Right x) - spreadChoice (Left x ) = Right (Left x) - instance ChoiceVector ((,) a) where spreadChoice (_, Left f) = Left f spreadChoice (x, Right y) = Right (x,y) -- Wasn't there a newtype somewhere with the elements flipped? --- --- More instances later, first some discussion. --- --- I'll have to freshen up on type system details to see how (or if) to do --- something like --- --- > instance (ChoiceVector a, ChoiceVector b) => ChoiceVector (a b) where --- > : --- --- But maybe it would be even better to use something like --- --- > class ChoiceVector v v' f | v -> v' f where --- > spreadChoice :: v -> Either f v' --- --- That way, more places in @v@ could spread the cheer, e.g.: --- --- As before: --- -- ( a , Either f b) (a , b) f --- > instance ChoiceVector ((,) a (Either f b)) ((,) a b) f where --- > spreadChoice (_, Left f) = Left f --- > spreadChoice (a, Right b) = Right (a,b) --- --- But also: --- -- ( Either f a , b) (a , b) f --- > instance ChoiceVector ((,) (Either f a) b) ((,) a b) f where --- > spreadChoice (Right a,b) = Right (a,b) --- > spreadChoice (Left f,_) = Left f --- --- And maybe even: --- -- ( Either f a , Either f b) (a , b) f --- > instance ChoiceVector ((,) (Either f a) (Either f b)) ((,) a b) f where --- > spreadChoice (Right a , Right b) = Right (a,b) --- > spreadChoice (Left f , _ ) = Left f --- > spreadChoice ( _ , Left f) = Left f --- --- Of course that would lead to a lot of overlapping instances... --- But I can't think of a different way. A selector function might help, --- but not even a "Data.Traversable" is powerful enough for that. --- But maybe someone has already solved all this with a lens library. --- --- Well, it's an interesting academic question. But for practical purposes, --- I have more than enough right now. - -instance ChoiceVector ((,,) a b) where - spreadChoice (_,_, Left f) = Left f - spreadChoice (a,b, Right x) = Right (a,b,x) - -instance ChoiceVector ((,,,) a b c) where - spreadChoice (_,_,_, Left f) = Left f - spreadChoice (a,b,c, Right x) = Right (a,b,c,x) - -instance ChoiceVector ((,,,,) a b c d) where - spreadChoice (_,_,_,_, Left f) = Left f - spreadChoice (a,b,c,d, Right x) = Right (a,b,c,d,x) - -instance ChoiceVector (Const a) where - spreadChoice (Const c) = Right (Const c) -- need to repackage because of implicit types - --- | Fails on the first error -instance ChoiceVector [] where - spreadChoice = sequence -- using the monad instance of Either. - -- Could be generalized to "Data.Traversable" - but why play - -- with UndecidableInstances unless this is really needed. - -- | Wrapper for a list. While the normal list instance of 'ChoiceVector' -- fails whenever it can, this type will never fail. newtype SuccessList a = SuccessList { collectNonFailing :: [a] } @@ -247,14 +129,3 @@ instance ChoiceVector SuccessList where collectRights :: [Either _l r] -> [r] collectRights = collectNonFailing . untag . spreadChoice . SuccessList where untag = fromLeft (error "Unexpected Left") - --- | A version of 'collectRights' generalized to other containers. The --- container must be both "reducible" and "buildable". Most general containers --- should fullfill these requirements, but there is no single typeclass --- (that I know of) for that. --- Therefore, they are split between 'Foldable' and 'MonadPlus'. --- (Note that 'Data.Traversable.Traversable' alone would not be enough, either.) -collectRightsF :: (F.Foldable c, MonadPlus c) => c (Either _l r) -> c r -collectRightsF = F.foldr unTagRight mzero - where unTagRight (Right x) = mplus $ return x - unTagRight _ = id diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs index 6c10ed61d..4af4242b6 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs @@ -38,8 +38,6 @@ module Text.Pandoc.Readers.Odt.Generic.Utils , uncurry4 , uncurry5 , uncurry6 -, uncurry7 -, uncurry8 , swap , reverseComposition , bool @@ -148,15 +146,11 @@ uncurry3 :: (a->b->c -> z) -> (a,b,c ) -> z uncurry4 :: (a->b->c->d -> z) -> (a,b,c,d ) -> z uncurry5 :: (a->b->c->d->e -> z) -> (a,b,c,d,e ) -> z uncurry6 :: (a->b->c->d->e->f -> z) -> (a,b,c,d,e,f ) -> z -uncurry7 :: (a->b->c->d->e->f->g -> z) -> (a,b,c,d,e,f,g ) -> z -uncurry8 :: (a->b->c->d->e->f->g->h -> z) -> (a,b,c,d,e,f,g,h) -> z uncurry3 fun (a,b,c ) = fun a b c uncurry4 fun (a,b,c,d ) = fun a b c d uncurry5 fun (a,b,c,d,e ) = fun a b c d e uncurry6 fun (a,b,c,d,e,f ) = fun a b c d e f -uncurry7 fun (a,b,c,d,e,f,g ) = fun a b c d e f g -uncurry8 fun (a,b,c,d,e,f,g,h) = fun a b c d e f g h swap :: (a,b) -> (b,a) swap (a,b) = (b,a) @@ -168,4 +162,3 @@ findBy :: (a -> Maybe b) -> [a] -> Maybe b findBy _ [] = Nothing findBy f ((f -> Just x):_ ) = Just x findBy f ( _:xs) = findBy f xs - diff --git a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs index 8c03d1a09..1c3e08a7f 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs @@ -41,50 +41,17 @@ module Text.Pandoc.Readers.Odt.Generic.XMLConverter , XMLConverterState , XMLConverter , FallibleXMLConverter -, swapPosition -, runConverter -, runConverter'' , runConverter' -, runConverterF' -, runConverterF -, getCurrentElement , getExtraState , setExtraState , modifyExtraState -, convertingExtraState , producingExtraState -, lookupNSiri -, lookupNSprefix -, readNSattributes -, elemName -, elemNameIs -, strContent -, elContent -, currentElem -, currentElemIs -, expectElement -, elChildren -, findChildren -, filterChildren -, filterChildrenName , findChild' -, findChild -, filterChild' -, filterChild -, filterChildName' -, filterChildName -, isSet , isSet' , isSetWithDefault -, hasAttrValueOf' -, failIfNotAttrValueOf -, isThatTheAttrValue -, searchAttrIn -, searchAttrWith , searchAttr , lookupAttr , lookupAttr' -, lookupAttrWithDefault , lookupDefaultingAttr , findAttr' , findAttr @@ -93,25 +60,9 @@ module Text.Pandoc.Readers.Odt.Generic.XMLConverter , readAttr' , readAttrWithDefault , getAttr --- , (>/<) --- , (?>/<) , executeIn -, collectEvery , withEveryL -, withEvery , tryAll -, tryAll' -, IdXMLConverter -, MaybeEConverter -, ElementMatchConverter -, MaybeCConverter -, ContentMatchConverter -, makeMatcherE -, makeMatcherC -, prepareMatchersE -, prepareMatchersC -, matchChildren -, matchContent'' , matchContent' , matchContent ) where @@ -121,7 +72,6 @@ import Control.Monad ( MonadPlus ) import Control.Arrow import qualified Data.Map as M -import qualified Data.Foldable as F import Data.Default import Data.Maybe @@ -210,17 +160,6 @@ currentElement state = head (parentElements state) -- | Replace the current position by another, modifying the extra state -- in the process -swapPosition :: (extraState -> extraState') - -> [XML.Element] - -> XMLConverterState nsID extraState - -> XMLConverterState nsID extraState' -swapPosition f stack state - = state { parentElements = stack - , moreState = f (moreState state) - } - --- | Replace the current position by another, modifying the extra state --- in the process swapStack' :: XMLConverterState nsID extraState -> [XML.Element] -> ( XMLConverterState nsID extraState , [XML.Element] ) @@ -264,14 +203,6 @@ runConverter :: XMLConverter nsID extraState input output -> output runConverter converter state input = snd $ runArrowState converter (state,input) --- -runConverter'' :: (NameSpaceID nsID) - => XMLConverter nsID extraState (Fallible ()) output - -> extraState - -> XML.Element - -> output -runConverter'' converter extraState element = runConverter (readNSattributes >>> converter) (createStartState element extraState) () - runConverter' :: (NameSpaceID nsID) => FallibleXMLConverter nsID extraState () success -> extraState @@ -280,20 +211,6 @@ runConverter' :: (NameSpaceID nsID) runConverter' converter extraState element = runConverter (readNSattributes >>? converter) (createStartState element extraState) () -- -runConverterF' :: FallibleXMLConverter nsID extraState x y - -> XMLConverterState nsID extraState - -> Fallible x -> Fallible y -runConverterF' a s e = runConverter (returnV e >>? a) s e - --- -runConverterF :: (NameSpaceID nsID) - => FallibleXMLConverter nsID extraState XML.Element x - -> extraState - -> Fallible XML.Element -> Fallible x -runConverterF a s = either failWith - (\e -> runConverter a (createStartState e s) e) - --- getCurrentElement :: XMLConverter nsID extraState x XML.Element getCurrentElement = extractFromState currentElement @@ -430,57 +347,15 @@ elemNameIs nsID name = keepingTheValue (lookupNSiri nsID) >>% hasThatName -------------------------------------------------------------------------------- -- -strContent :: XMLConverter nsID extraState x String -strContent = getCurrentElement - >>^ XML.strContent - --- elContent :: XMLConverter nsID extraState x [XML.Content] elContent = getCurrentElement >>^ XML.elContent -------------------------------------------------------------------------------- --- Current element --------------------------------------------------------------------------------- - --- -currentElem :: XMLConverter nsID extraState x (XML.QName) -currentElem = getCurrentElement - >>^ XML.elName - -currentElemIs :: (NameSpaceID nsID) - => nsID -> ElementName - -> XMLConverter nsID extraState x Bool -currentElemIs nsID name = getCurrentElement - >>> elemNameIs nsID name - - - -{- -currentElemIs'' nsID name = ( (getCurrentElement >>^ XML.elName >>> - (XML.qName >>^ (&&).(== name) ) - ^&&&^ - (XML.qIRI >>^ (==) ) - ) >>% (.) - ) &&& lookupNSiri nsID >>% ($) --} - --- -expectElement :: (NameSpaceID nsID) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState x () -expectElement nsID name = currentElemIs nsID name - >>^ boolToChoice - --------------------------------------------------------------------------------- -- Chilren -------------------------------------------------------------------------------- -- -elChildren :: XMLConverter nsID extraState x [XML.Element] -elChildren = getCurrentElement - >>^ XML.elChildren - -- findChildren :: (NameSpaceID nsID) => nsID -> ElementName @@ -490,18 +365,6 @@ findChildren nsID name = elemName nsID name >>% XML.findChildren -- -filterChildren :: (XML.Element -> Bool) - -> XMLConverter nsID extraState x [XML.Element] -filterChildren p = getCurrentElement - >>^ XML.filterChildren p - --- -filterChildrenName :: (XML.QName -> Bool) - -> XMLConverter nsID extraState x [XML.Element] -filterChildrenName p = getCurrentElement - >>^ XML.filterChildrenName p - --- findChild' :: (NameSpaceID nsID) => nsID -> ElementName @@ -517,45 +380,12 @@ findChild :: (NameSpaceID nsID) findChild nsID name = findChild' nsID name >>> maybeToChoice --- -filterChild' :: (XML.Element -> Bool) - -> XMLConverter nsID extraState x (Maybe XML.Element) -filterChild' p = getCurrentElement - >>^ XML.filterChild p - --- -filterChild :: (XML.Element -> Bool) - -> FallibleXMLConverter nsID extraState x XML.Element -filterChild p = filterChild' p - >>> maybeToChoice - --- -filterChildName' :: (XML.QName -> Bool) - -> XMLConverter nsID extraState x (Maybe XML.Element) -filterChildName' p = getCurrentElement - >>^ XML.filterChildName p - --- -filterChildName :: (XML.QName -> Bool) - -> FallibleXMLConverter nsID extraState x XML.Element -filterChildName p = filterChildName' p - >>> maybeToChoice - -------------------------------------------------------------------------------- -- Attributes -------------------------------------------------------------------------------- -- -isSet :: (NameSpaceID nsID) - => nsID -> AttributeName - -> (Either Failure Bool) - -> FallibleXMLConverter nsID extraState x Bool -isSet nsID attrName deflt - = findAttr' nsID attrName - >>^ maybe deflt stringToBool - --- isSet' :: (NameSpaceID nsID) => nsID -> AttributeName -> XMLConverter nsID extraState x (Maybe Bool) @@ -570,34 +400,6 @@ isSetWithDefault nsID attrName def' = isSet' nsID attrName >>^ fromMaybe def' --- -hasAttrValueOf' :: (NameSpaceID nsID) - => nsID -> AttributeName - -> AttributeValue - -> XMLConverter nsID extraState x Bool -hasAttrValueOf' nsID attrName attrValue - = findAttr nsID attrName - >>> ( const False ^|||^ (==attrValue)) - --- -failIfNotAttrValueOf :: (NameSpaceID nsID) - => nsID -> AttributeName - -> AttributeValue - -> FallibleXMLConverter nsID extraState x () -failIfNotAttrValueOf nsID attrName attrValue - = hasAttrValueOf' nsID attrName attrValue - >>^ boolToChoice - --- | Is the value that is currently transported in the arrow the value of --- the specified attribute? -isThatTheAttrValue :: (NameSpaceID nsID) - => nsID -> AttributeName - -> FallibleXMLConverter nsID extraState AttributeValue Bool -isThatTheAttrValue nsID attrName - = keepingTheValue - (findAttr nsID attrName) - >>% right.(==) - -- | Lookup value in a dictionary, fail if no attribute found or value -- not in dictionary searchAttrIn :: (NameSpaceID nsID) @@ -608,18 +410,6 @@ searchAttrIn nsID attrName dict = findAttr nsID attrName >>?^? maybeToChoice.(`lookup` dict ) - --- | Lookup value in a dictionary. Fail if no attribute found. If value not in --- dictionary, return default value -searchAttrWith :: (NameSpaceID nsID) - => nsID -> AttributeName - -> a - -> [(AttributeValue,a)] - -> FallibleXMLConverter nsID extraState x a -searchAttrWith nsID attrName defV dict - = findAttr nsID attrName - >>?^ (fromMaybe defV).(`lookup` dict ) - -- | Lookup value in a dictionary. If attribute or value not found, -- return default value searchAttr :: (NameSpaceID nsID) @@ -789,16 +579,6 @@ prepareIteration nsID name = keepingTheValue (findChildren nsID name) >>% distributeValue --- | Applies a converter to every child element of a specific type. --- Collects results in a 'Monoid'. --- Fails completely if any conversion fails. -collectEvery :: (NameSpaceID nsID, Monoid m) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState a m - -> FallibleXMLConverter nsID extraState a m -collectEvery nsID name a = prepareIteration nsID name - >>> foldS' (switchingTheStack a) - -- withEveryL :: (NameSpaceID nsID) => nsID -> ElementName @@ -826,16 +606,6 @@ tryAll nsID name a = prepareIteration nsID name >>> iterateS (switchingTheStack a) >>^ collectRights --- | Applies a converter to every child element of a specific type. --- Collects all successful results. -tryAll' :: (NameSpaceID nsID, F.Foldable c, MonadPlus c) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState b a - -> XMLConverter nsID extraState b (c a) -tryAll' nsID name a = prepareIteration nsID name - >>> iterateS (switchingTheStack a) - >>^ collectRightsF - -------------------------------------------------------------------------------- -- Matching children -------------------------------------------------------------------------------- @@ -843,15 +613,6 @@ tryAll' nsID name a = prepareIteration nsID name type IdXMLConverter nsID moreState x = XMLConverter nsID moreState x x -type MaybeEConverter nsID moreState x - = Maybe (IdXMLConverter nsID moreState (x, XML.Element)) - --- Chainable converter that helps deciding which converter to actually use. -type ElementMatchConverter nsID extraState x - = IdXMLConverter nsID - extraState - (MaybeEConverter nsID extraState x, XML.Element) - type MaybeCConverter nsID moreState x = Maybe (IdXMLConverter nsID moreState (x, XML.Content)) @@ -862,26 +623,6 @@ type ContentMatchConverter nsID extraState x (MaybeCConverter nsID extraState x, XML.Content) -- Helper function: The @c@ is actually a converter that is to be selected by --- matching XML elements to the first two parameters. --- The fold used to match elements however is very simple, so to use it, --- this function wraps the converter in another converter that unifies --- the accumulator. Think of a lot of converters with the resulting type --- chained together. The accumulator not only transports the element --- unchanged to the next matcher, it also does the actual selecting by --- combining the intermediate results with '(<|>)'. -makeMatcherE :: (NameSpaceID nsID) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState a a - -> ElementMatchConverter nsID extraState a -makeMatcherE nsID name c = ( second ( - elemNameIs nsID name - >>^ bool Nothing (Just tryC) - ) - >>% (<|>) - ) &&&^ snd - where tryC = (fst ^&&& executeThere c >>% recover) &&&^ snd - --- Helper function: The @c@ is actually a converter that is to be selected by -- matching XML content to the first two parameters. -- The fold used to match elements however is very simple, so to use it, -- this function wraps the converter in another converter that unifies @@ -914,13 +655,6 @@ makeMatcherC nsID name c = ( second ( contentToElem _ -> failEmpty -- Creates and chains a bunch of matchers -prepareMatchersE :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)] - -> ElementMatchConverter nsID extraState x ---prepareMatchersE = foldSs . (map $ uncurry3 makeMatcherE) -prepareMatchersE = reverseComposition . (map $ uncurry3 makeMatcherE) - --- Creates and chains a bunch of matchers prepareMatchersC :: (NameSpaceID nsID) => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)] -> ContentMatchConverter nsID extraState x @@ -928,52 +662,6 @@ prepareMatchersC :: (NameSpaceID nsID) prepareMatchersC = reverseComposition . (map $ uncurry3 makeMatcherC) -- | Takes a list of element-data - converter groups and --- * Finds all children of the current element --- * Matches each group to each child in order (at most one group per child) --- * Filters non-matched children --- * Chains all found converters in child-order --- * Applies the chain to the input element -matchChildren :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] - -> XMLConverter nsID extraState a a -matchChildren lookups = let matcher = prepareMatchersE lookups - in keepingTheValue ( - elChildren - >>> map (Nothing,) - ^>> iterateSL matcher - >>^ catMaybes.map (\(m,e) -> fmap (swallowElem e) m) - -- >>> foldSs - >>> reverseComposition - ) - >>> swap - ^>> app - where - -- let the converter swallow the element and drop the element - -- in the return value - swallowElem element converter = (,element) ^>> converter >>^ fst - --- -matchContent'' :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] - -> XMLConverter nsID extraState a a -matchContent'' lookups = let matcher = prepareMatchersC lookups - in keepingTheValue ( - elContent - >>> map (Nothing,) - ^>> iterateSL matcher - >>^ catMaybes.map (\(m,c) -> fmap (swallowContent c) m) - -- >>> foldSs - >>> reverseComposition - ) - >>> swap - ^>> app - where - -- let the converter swallow the content and drop the content - -- in the return value - swallowContent content converter = (,content) ^>> converter >>^ fst - - --- | Takes a list of element-data - converter groups and -- * Finds all content of the current element -- * Matches each group to each piece of content in order -- (at most one group per piece of content) @@ -1018,14 +706,6 @@ matchContent lookups fallback -- Internals -------------------------------------------------------------------------------- -stringToBool :: (Monoid failure) => String -> Either failure Bool -stringToBool val -- stringToBool' val >>> maybeToChoice - | val `elem` trueValues = succeedWith True - | val `elem` falseValues = succeedWith False - | otherwise = failEmpty - where trueValues = ["true" ,"on" ,"1"] - falseValues = ["false","off","0"] - stringToBool' :: String -> Maybe Bool stringToBool' val | val `elem` trueValues = Just True | val `elem` falseValues = Just False diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 26ba6df82..87a6dc91c 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -50,23 +50,11 @@ module Text.Pandoc.Readers.Odt.StyleReader , ListLevelType (..) , LengthOrPercent (..) , lookupStyle -, getTextProperty -, getTextProperty' -, getParaProperty -, getListStyle , getListLevelStyle , getStyleFamily -, lookupDefaultStyle , lookupDefaultStyle' , lookupListStyleByName -, getPropertyChain -, textPropertyChain -, stylePropertyChain -, stylePropertyChain' -, getStylePropertyChain , extendedStylePropertyChain -, extendedStylePropertyChain' -, liftStyles , readStylesAt ) where @@ -83,7 +71,6 @@ import Data.Maybe import qualified Text.XML.Light as XML -import Text.Pandoc.Readers.Odt.Arrows.State import Text.Pandoc.Readers.Odt.Arrows.Utils import Text.Pandoc.Readers.Odt.Generic.Utils @@ -624,20 +611,11 @@ lookupStyle :: StyleName -> Styles -> Maybe Style lookupStyle name Styles{..} = M.lookup name stylesByName -- -lookupDefaultStyle :: StyleFamily -> Styles -> StyleProperties -lookupDefaultStyle family Styles{..} = fromMaybe def - (M.lookup family defaultStyleMap) - --- lookupDefaultStyle' :: Styles -> StyleFamily -> StyleProperties lookupDefaultStyle' Styles{..} family = fromMaybe def (M.lookup family defaultStyleMap) -- -getListStyle :: Style -> Styles -> Maybe ListStyle -getListStyle Style{..} styles = listStyle >>= (`lookupListStyleByName` styles) - --- lookupListStyleByName :: StyleName -> Styles -> Maybe ListStyle lookupListStyleByName name Styles{..} = M.lookup name listStylesByName @@ -681,64 +659,3 @@ extendedStylePropertyChain [style] styles = (stylePropertyChain style s ++ (maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles))) extendedStylePropertyChain (style:trace) styles = (stylePropertyChain style styles) ++ (extendedStylePropertyChain trace styles) --- Optimizable with Data.Sequence - --- -extendedStylePropertyChain' :: [Style] -> Styles -> Maybe [StyleProperties] -extendedStylePropertyChain' [] _ = Nothing -extendedStylePropertyChain' [style] styles = Just ( - (stylePropertyChain style styles) - ++ (maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles))) - ) -extendedStylePropertyChain' (style:trace) styles = fmap ((stylePropertyChain style styles) ++) - (extendedStylePropertyChain' trace styles) - --- -stylePropertyChain' :: Styles -> Style -> [StyleProperties] -stylePropertyChain' = flip stylePropertyChain - --- -getStylePropertyChain :: StyleName -> Styles -> [StyleProperties] -getStylePropertyChain name styles = maybe [] - (`stylePropertyChain` styles) - (lookupStyle name styles) - --- -getPropertyChain :: (StyleProperties -> Maybe a) -> Style -> Styles -> [a] -getPropertyChain extract style styles = catMaybes - $ map extract - $ stylePropertyChain style styles - --- -textPropertyChain :: Style -> Styles -> [TextProperties] -textPropertyChain = getPropertyChain textProperties - --- -paraPropertyChain :: Style -> Styles -> [ParaProperties] -paraPropertyChain = getPropertyChain paraProperties - --- -getTextProperty :: (TextProperties -> a) -> Style -> Styles -> Maybe a -getTextProperty extract style styles = fmap extract - $ listToMaybe - $ textPropertyChain style styles - --- -getTextProperty' :: (TextProperties -> Maybe a) -> Style -> Styles -> Maybe a -getTextProperty' extract style styles = F.asum - $ map extract - $ textPropertyChain style styles - --- -getParaProperty :: (ParaProperties -> a) -> Style -> Styles -> Maybe a -getParaProperty extract style styles = fmap extract - $ listToMaybe - $ paraPropertyChain style styles - --- | Lifts the reader into another readers' state. -liftStyles :: (OdtConverterState s -> OdtConverterState Styles) - -> (OdtConverterState Styles -> OdtConverterState s ) - -> XMLReader s x x -liftStyles extract inject = switchState extract inject - $ convertingExtraState M.empty readAllStyles - diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 5e509178d..5e0d67d10 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -40,15 +40,18 @@ import Text.Pandoc.Parsing (reportLogMessages) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) +import Data.Text (Text) +import qualified Data.Text as T -- | Parse org-mode string and return a Pandoc document. readOrg :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readOrg opts s = do parsed <- flip runReaderT def $ - readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n") + readWithM parseOrg (optionsToParserState opts) + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left _ -> throwError $ PandocParseError "problem parsing org" diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index cc2e82d5b..9c6614c99 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,8 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.BlockStarts + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -61,8 +61,12 @@ headerStart = try $ tableStart :: Monad m => OrgParser m Char tableStart = try $ skipSpaces *> char '|' +gridTableStart :: Monad m => OrgParser m () +gridTableStart = try $ skipSpaces <* char '+' <* char '-' + + latexEnvStart :: Monad m => OrgParser m String -latexEnvStart = try $ do +latexEnvStart = try $ skipSpaces *> string "\\begin{" *> latexEnvName <* string "}" @@ -93,8 +97,7 @@ orderedListStart = genericListStart orderedListMarker where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)") drawerStart :: Monad m => OrgParser m String -drawerStart = try $ - skipSpaces *> drawerName <* skipSpaces <* newline +drawerStart = try $ skipSpaces *> drawerName <* skipSpaces <* newline where drawerName = char ':' *> manyTill nonspaceChar (char ':') metaLineStart :: Monad m => OrgParser m () @@ -116,8 +119,8 @@ noteMarker = try $ do -- | Succeeds if the parser is at the end of a block. endOfBlock :: Monad m => OrgParser m () -endOfBlock = lookAhead . try $ do - void blankline <|> anyBlockStart +endOfBlock = lookAhead . try $ + void blankline <|> anyBlockStart where -- Succeeds if there is a new block starting at this position. anyBlockStart :: Monad m => OrgParser m () @@ -126,6 +129,7 @@ endOfBlock = lookAhead . try $ do , hline , metaLineStart , commentLineStart + , gridTableStart , void noteMarker , void tableStart , void drawerStart @@ -134,4 +138,3 @@ endOfBlock = lookAhead . try $ do , void bulletListStart , void orderedListStart ] - diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index b0a19b833..3e0ab0127 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -18,9 +15,10 @@ 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 -} - +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RecordWildCards #-} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.Blocks Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above @@ -34,6 +32,7 @@ module Text.Pandoc.Readers.Org.Blocks ) where import Text.Pandoc.Readers.Org.BlockStarts +import Text.Pandoc.Readers.Org.DocumentTree (documentTree, headlineToBlocks) import Text.Pandoc.Readers.Org.Inlines import Text.Pandoc.Readers.Org.Meta (metaExport, metaKey, metaLine) import Text.Pandoc.Readers.Org.ParserState @@ -52,211 +51,21 @@ import Control.Monad (foldM, guard, mzero, void) import Data.Char (isSpace, toLower, toUpper) import Data.Default (Default) import Data.List (foldl', isPrefixOf) -import Data.Maybe (fromMaybe, isNothing) +import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) -- --- Org headers --- -newtype Tag = Tag { fromTag :: String } - deriving (Show, Eq) - --- | Create a tag containing the given string. -toTag :: String -> Tag -toTag = Tag - --- | The key (also called name or type) of a property. -newtype PropertyKey = PropertyKey { fromKey :: String } - deriving (Show, Eq, Ord) - --- | Create a property key containing the given string. Org mode keys are --- case insensitive and are hence converted to lower case. -toPropertyKey :: String -> PropertyKey -toPropertyKey = PropertyKey . map toLower - --- | The value assigned to a property. -newtype PropertyValue = PropertyValue { fromValue :: String } - --- | Create a property value containing the given string. -toPropertyValue :: String -> PropertyValue -toPropertyValue = PropertyValue - --- | Check whether the property value is non-nil (i.e. truish). -isNonNil :: PropertyValue -> Bool -isNonNil p = map toLower (fromValue p) `notElem` ["()", "{}", "nil"] - --- | Key/value pairs from a PROPERTIES drawer -type Properties = [(PropertyKey, PropertyValue)] - --- | Org mode headline (i.e. a document subtree). -data Headline = Headline - { headlineLevel :: Int - , headlineTodoMarker :: Maybe TodoMarker - , headlineText :: Inlines - , headlineTags :: [Tag] - , headlineProperties :: Properties - , headlineContents :: Blocks - , headlineChildren :: [Headline] - } - --- --- Parsing headlines and subtrees --- - --- | Read an Org mode headline and its contents (i.e. a document subtree). --- @lvl@ gives the minimum acceptable level of the tree. -headline :: PandocMonad m => Int -> OrgParser m (F Headline) -headline lvl = try $ do - level <- headerStart - guard (lvl <= level) - todoKw <- optionMaybe todoKeyword - title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle - tags <- option [] headerTags - newline - properties <- option mempty propertiesDrawer - contents <- blocks - children <- many (headline (level + 1)) - return $ do - title' <- title - contents' <- contents - children' <- sequence children - return $ Headline - { headlineLevel = level - , headlineTodoMarker = todoKw - , headlineText = title' - , headlineTags = tags - , headlineProperties = properties - , headlineContents = contents' - , headlineChildren = children' - } - where - endOfTitle :: Monad m => OrgParser m () - endOfTitle = void . lookAhead $ optional headerTags *> newline - - headerTags :: Monad m => OrgParser m [Tag] - headerTags = try $ - let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':' - in map toTag <$> (skipSpaces *> char ':' *> many1 tag <* skipSpaces) - --- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks -headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks -headlineToBlocks hdln@(Headline {..}) = do - maxHeadlineLevels <- getExportSetting exportHeadlineLevels - case () of - _ | any isNoExportTag headlineTags -> return mempty - _ | any isArchiveTag headlineTags -> archivedHeadlineToBlocks hdln - _ | isCommentTitle headlineText -> return mempty - _ | headlineLevel >= maxHeadlineLevels -> headlineToHeaderWithList hdln - _ | otherwise -> headlineToHeaderWithContents hdln - -isNoExportTag :: Tag -> Bool -isNoExportTag = (== toTag "noexport") - -isArchiveTag :: Tag -> Bool -isArchiveTag = (== toTag "ARCHIVE") - --- | Check if the title starts with COMMENT. --- FIXME: This accesses builder internals not intended for use in situations --- like these. Replace once keyword parsing is supported. -isCommentTitle :: Inlines -> Bool -isCommentTitle (B.toList -> (Str "COMMENT":_)) = True -isCommentTitle _ = False - -archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks -archivedHeadlineToBlocks hdln = do - archivedTreesOption <- getExportSetting exportArchivedTrees - case archivedTreesOption of - ArchivedTreesNoExport -> return mempty - ArchivedTreesExport -> headlineToHeaderWithContents hdln - ArchivedTreesHeadlineOnly -> headlineToHeader hdln - -headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks -headlineToHeaderWithList hdln@(Headline {..}) = do - maxHeadlineLevels <- getExportSetting exportHeadlineLevels - header <- headlineToHeader hdln - listElements <- sequence (map headlineToBlocks headlineChildren) - let listBlock = if null listElements - then mempty - else B.orderedList listElements - let headerText = if maxHeadlineLevels == headlineLevel - then header - else flattenHeader header - return $ headerText <> headlineContents <> listBlock - where - flattenHeader :: Blocks -> Blocks - flattenHeader blks = - case B.toList blks of - (Header _ _ inlns:_) -> B.para (B.fromList inlns) - _ -> mempty - -headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks -headlineToHeaderWithContents hdln@(Headline {..}) = do - header <- headlineToHeader hdln - childrenBlocks <- mconcat <$> sequence (map headlineToBlocks headlineChildren) - return $ header <> headlineContents <> childrenBlocks - -headlineToHeader :: Monad m => Headline -> OrgParser m Blocks -headlineToHeader (Headline {..}) = do - exportTodoKeyword <- getExportSetting exportWithTodoKeywords - let todoText = if exportTodoKeyword - then case headlineTodoMarker of - Just kw -> todoKeywordToInlines kw <> B.space - Nothing -> mempty - else mempty - let text = tagTitle (todoText <> headlineText) headlineTags - let propAttr = propertiesToAttr headlineProperties - attr <- registerHeader propAttr headlineText - return $ B.headerWith attr headlineLevel text - -todoKeyword :: Monad m => OrgParser m TodoMarker -todoKeyword = try $ do - taskStates <- activeTodoMarkers <$> getState - let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar) - choice (map kwParser taskStates) - -todoKeywordToInlines :: TodoMarker -> Inlines -todoKeywordToInlines tdm = - let todoText = todoMarkerName tdm - todoState = map toLower . show $ todoMarkerState tdm - classes = [todoState, todoText] - in B.spanWith (mempty, classes, mempty) (B.str todoText) - -propertiesToAttr :: Properties -> Attr -propertiesToAttr properties = - let - toStringPair prop = (fromKey (fst prop), fromValue (snd prop)) - customIdKey = toPropertyKey "custom_id" - classKey = toPropertyKey "class" - unnumberedKey = toPropertyKey "unnumbered" - specialProperties = [customIdKey, classKey, unnumberedKey] - id' = fromMaybe mempty . fmap fromValue . lookup customIdKey $ properties - cls = fromMaybe mempty . fmap fromValue . lookup classKey $ properties - kvs' = map toStringPair . filter ((`notElem` specialProperties) . fst) - $ properties - isUnnumbered = - fromMaybe False . fmap isNonNil . lookup unnumberedKey $ properties - in - (id', words cls ++ (if isUnnumbered then ["unnumbered"] else []), kvs') - -tagTitle :: Inlines -> [Tag] -> Inlines -tagTitle title tags = title <> (mconcat $ map tagToInline tags) - -tagToInline :: Tag -> Inlines -tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty - - --- -- parsing blocks -- -- | Get a list of blocks. blockList :: PandocMonad m => OrgParser m [Block] blockList = do - initialBlocks <- blocks - headlines <- sequence <$> manyTill (headline 1) eof + headlines <- documentTree blocks inline st <- getState - headlineBlocks <- fmap mconcat . sequence . map headlineToBlocks $ runF headlines st - return . B.toList $ (runF initialBlocks st) <> headlineBlocks + headlineBlocks <- headlineToBlocks $ runF headlines st + -- ignore first headline, it's the document's title + return . drop 1 . B.toList $ headlineBlocks -- | Get the meta information saved in the state. meta :: Monad m => OrgParser m Meta @@ -274,6 +83,7 @@ block = choice [ mempty <$ blanklines , figure , example , genericDrawer + , include , specialLine , horizontalRule , list @@ -302,7 +112,7 @@ data BlockAttributes = BlockAttributes -- | Convert BlockAttributes into pandoc Attr attrFromBlockAttributes :: BlockAttributes -> Attr -attrFromBlockAttributes (BlockAttributes{..}) = +attrFromBlockAttributes BlockAttributes{..} = let ident = fromMaybe mempty $ lookup "id" blockAttrKeyValues classes = case lookup "class" blockAttrKeyValues of @@ -311,18 +121,18 @@ attrFromBlockAttributes (BlockAttributes{..}) = kv = filter ((`notElem` ["id", "class"]) . fst) blockAttrKeyValues in (ident, classes, kv) -stringyMetaAttribute :: Monad m => (String -> Bool) -> OrgParser m (String, String) -stringyMetaAttribute attrCheck = try $ do +stringyMetaAttribute :: Monad m => OrgParser m (String, String) +stringyMetaAttribute = try $ do metaLineStart attrName <- map toUpper <$> many1Till nonspaceChar (char ':') - guard $ attrCheck attrName skipSpaces - attrValue <- anyLine + attrValue <- anyLine <|> ("" <$ newline) return (attrName, attrValue) blockAttributes :: PandocMonad m => OrgParser m BlockAttributes blockAttributes = try $ do - kv <- many (stringyMetaAttribute attrCheck) + kv <- many stringyMetaAttribute + guard $ all (attrCheck . fst) kv let caption = foldl' (appendValues "CAPTION") Nothing kv let kvAttrs = foldl' (appendValues "ATTR_HTML") Nothing kv let name = lookup "NAME" kv @@ -331,7 +141,7 @@ blockAttributes = try $ do Nothing -> return Nothing Just s -> Just <$> parseFromString inlines (s ++ "\n") kvAttrs' <- parseFromString keyValues . (++ "\n") $ fromMaybe mempty kvAttrs - return $ BlockAttributes + return BlockAttributes { blockAttrName = name , blockAttrLabel = label , blockAttrCaption = caption' @@ -339,13 +149,7 @@ blockAttributes = try $ do } where attrCheck :: String -> Bool - attrCheck attr = - case attr of - "NAME" -> True - "LABEL" -> True - "CAPTION" -> True - "ATTR_HTML" -> True - _ -> False + attrCheck x = x `elem` ["NAME", "LABEL", "CAPTION", "ATTR_HTML", "RESULTS"] appendValues :: String -> Maybe String -> (String, String) -> Maybe String appendValues attrName accValue (key, value) = @@ -355,6 +159,7 @@ blockAttributes = try $ do Just acc -> Just $ acc ++ ' ':value Nothing -> Just value +-- | Parse key-value pairs for HTML attributes keyValues :: Monad m => OrgParser m [(String, String)] keyValues = try $ manyTill ((,) <$> key <*> value) newline @@ -381,7 +186,7 @@ orgBlock = try $ do blockAttrs <- blockAttributes blkType <- blockHeaderStart ($ blkType) $ - case (map toLower blkType) of + case map toLower blkType of "export" -> exportBlock "comment" -> rawBlockLines (const mempty) "html" -> rawBlockLines (return . B.rawBlock (lowercase blkType)) @@ -402,10 +207,10 @@ orgBlock = try $ do lowercase = map toLower rawBlockLines :: Monad m => (String -> F Blocks) -> String -> OrgParser m (F Blocks) -rawBlockLines f blockType = (ignHeaders *> (f <$> rawBlockContent blockType)) +rawBlockLines f blockType = ignHeaders *> (f <$> rawBlockContent blockType) parseBlockLines :: PandocMonad m => (F Blocks -> F Blocks) -> String -> OrgParser m (F Blocks) -parseBlockLines f blockType = (ignHeaders *> (f <$> parsedBlockContent)) +parseBlockLines f blockType = ignHeaders *> (f <$> parsedBlockContent) where parsedBlockContent :: PandocMonad m => OrgParser m (F Blocks) parsedBlockContent = try $ do @@ -433,8 +238,7 @@ rawBlockContent blockType = try $ do stripIndent strs = map (drop (shortestIndent strs)) strs shortestIndent :: [String] -> Int - shortestIndent = foldr min maxBound - . map (length . takeWhile isSpace) + shortestIndent = foldr (min . length . takeWhile isSpace) maxBound . filter (not . null) tabsToSpaces :: Int -> String -> String @@ -442,7 +246,7 @@ rawBlockContent blockType = try $ do tabsToSpaces tabLen cs'@(c:cs) = case c of ' ' -> ' ':tabsToSpaces tabLen cs - '\t' -> (take tabLen $ repeat ' ') ++ tabsToSpaces tabLen cs + '\t' -> replicate tabLen ' ' ++ tabsToSpaces tabLen cs _ -> cs' commaEscaped :: String -> String @@ -490,16 +294,15 @@ codeBlock blockAttrs blockType = do skipSpaces (classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders) content <- rawBlockContent blockType - resultsContent <- trailingResultsBlock + resultsContent <- option mempty babelResultsBlock let id' = fromMaybe mempty $ blockAttrName blockAttrs let codeBlck = B.codeBlockWith ( id', classes, kv ) content let labelledBlck = maybe (pure codeBlck) (labelDiv codeBlck) (blockAttrCaption blockAttrs) - let resultBlck = fromMaybe mempty resultsContent return $ - (if exportsCode kv then labelledBlck else mempty) <> - (if exportsResults kv then resultBlck else mempty) + (if exportsCode kv then labelledBlck else mempty) <> + (if exportsResults kv then resultsContent else mempty) where labelDiv :: Blocks -> F Inlines -> F Blocks labelDiv blk value = @@ -514,12 +317,16 @@ codeBlock blockAttrs blockType = do exportsResults :: [(String, String)] -> Bool exportsResults = maybe False (`elem` ["results", "both"]) . lookup "exports" -trailingResultsBlock :: PandocMonad m => OrgParser m (Maybe (F Blocks)) -trailingResultsBlock = optionMaybe . try $ do +-- | Parse the result of an evaluated babel code block. +babelResultsBlock :: PandocMonad m => OrgParser m (F Blocks) +babelResultsBlock = try $ do blanklines - stringAnyCase "#+RESULTS:" - blankline + resultsMarker <|> + (lookAhead . void . try $ + manyTill (metaLineStart *> anyLineNewline) resultsMarker) block + where + resultsMarker = try . void $ stringAnyCase "#+RESULTS:" *> blankline -- | Parse code block arguments codeHeaderArgs :: Monad m => OrgParser m ([String], [(String, String)]) @@ -527,13 +334,13 @@ codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord (switchClasses, switchKv) <- switchesAsAttributes parameters <- manyTill blockOption newline - return $ ( translateLang language : switchClasses - , originalLang language <> switchKv <> parameters - ) + return ( translateLang language : switchClasses + , originalLang language <> switchKv <> parameters + ) switchesAsAttributes :: Monad m => OrgParser m ([String], [(String, String)]) switchesAsAttributes = try $ do - switches <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar)) + switches <- skipSpaces *> try (switch `sepBy` many1 spaceChar) return $ foldr addToAttr ([], []) switches where addToAttr :: (Char, Maybe String, SwitchPolarity) @@ -541,10 +348,10 @@ switchesAsAttributes = try $ do -> ([String], [(String, String)]) addToAttr ('n', lineNum, pol) (cls, kv) = let kv' = case lineNum of - Just num -> (("startFrom", num):kv) + Just num -> ("startFrom", num):kv Nothing -> kv cls' = case pol of - SwitchPlus -> "continuedSourceBlock":cls + SwitchPlus -> "continuedSourceBlock":cls SwitchMinus -> cls in ("numberLines":cls', kv') addToAttr _ x = x @@ -573,7 +380,7 @@ genericSwitch :: Monad m genericSwitch c p = try $ do polarity <- switchPolarity <* char c <* skipSpaces arg <- optionMaybe p - return $ (c, arg, polarity) + return (c, arg, polarity) -- | Reads a line number switch option. The line number switch can be used with -- example and source blocks. @@ -593,8 +400,8 @@ orgParamValue = try $ *> noneOf "\n\r" `many1Till` endOfValue <* skipSpaces where - endOfValue = lookAhead $ (try $ skipSpaces <* oneOf "\n\r") - <|> (try $ skipSpaces1 <* orgArgKey) + endOfValue = lookAhead $ try (skipSpaces <* oneOf "\n\r") + <|> try (skipSpaces1 <* orgArgKey) -- @@ -612,7 +419,7 @@ genericDrawer = try $ do -- Include drawer if it is explicitly included in or not explicitly excluded -- from the list of drawers that should be exported. PROPERTIES drawers are -- never exported. - case (exportDrawers . orgStateExportSettings $ state) of + case exportDrawers . orgStateExportSettings $ state of _ | name == "PROPERTIES" -> return mempty Left names | name `elem` names -> return mempty Right names | name `notElem` names -> return mempty @@ -631,25 +438,6 @@ drawerEnd :: Monad m => OrgParser m String drawerEnd = try $ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline --- | Read a :PROPERTIES: drawer and return the key/value pairs contained --- within. -propertiesDrawer :: Monad m => OrgParser m Properties -propertiesDrawer = try $ do - drawerType <- drawerStart - guard $ map toUpper drawerType == "PROPERTIES" - manyTill property (try drawerEnd) - where - property :: Monad m => OrgParser m (PropertyKey, PropertyValue) - property = try $ (,) <$> key <*> value - - key :: Monad m => OrgParser m PropertyKey - key = fmap toPropertyKey . try $ - skipSpaces *> char ':' *> many1Till nonspaceChar (char ':') - - value :: Monad m => OrgParser m PropertyValue - value = fmap toPropertyValue . try $ - skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline) - -- -- Figures @@ -665,7 +453,7 @@ figure = try $ do Nothing -> mzero Just imgSrc -> do guard (isImageFilename imgSrc) - let isFigure = not . isNothing $ blockAttrCaption figAttrs + let isFigure = isJust $ blockAttrCaption figAttrs return $ imageBlock isFigure figAttrs imgSrc where selfTarget :: PandocMonad m => OrgParser m String @@ -700,8 +488,7 @@ endOfParagraph = try $ skipSpaces *> newline *> endOfBlock -- | Example code marked up by a leading colon. example :: Monad m => OrgParser m (F Blocks) -example = try $ do - returnF . exampleCode =<< unlines <$> many1 exampleLine +example = try $ returnF . exampleCode =<< unlines <$> many1 exampleLine where exampleLine :: Monad m => OrgParser m String exampleLine = try $ exampleLineStart *> anyLine @@ -717,6 +504,34 @@ exampleCode = B.codeBlockWith ("", ["example"], []) specialLine :: PandocMonad m => OrgParser m (F Blocks) specialLine = fmap return . try $ rawExportLine <|> metaLine <|> commentLine +-- | Include the content of a file. +include :: PandocMonad m => OrgParser m (F Blocks) +include = try $ do + metaLineStart <* stringAnyCase "include:" <* skipSpaces + filename <- includeTarget + blockType <- optionMaybe $ skipSpaces *> many1 alphaNum + blocksParser <- case blockType of + Just "example" -> + return $ pure . B.codeBlock <$> parseRaw + Just "export" -> do + format <- skipSpaces *> many (noneOf "\n\r\t ") + return $ pure . B.rawBlock format <$> parseRaw + Just "src" -> do + language <- skipSpaces *> many (noneOf "\n\r\t ") + let attr = (mempty, [language], mempty) + return $ pure . B.codeBlockWith attr <$> parseRaw + _ -> return $ pure . B.fromList <$> blockList + anyLine + insertIncludedFileF blocksParser ["."] filename + where + includeTarget :: PandocMonad m => OrgParser m FilePath + includeTarget = do + char '"' + manyTill (noneOf "\n\r\t") (char '"') + + parseRaw :: PandocMonad m => OrgParser m String + parseRaw = many anyChar + rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart @@ -755,11 +570,15 @@ data OrgTable = OrgTable } table :: PandocMonad m => OrgParser m (F Blocks) -table = try $ do +table = gridTableWith blocks True <|> orgTable + +-- | A normal org table +orgTable :: PandocMonad m => OrgParser m (F Blocks) +orgTable = try $ do -- don't allow a table on the first line of a list item; org requires that -- tables start at first non-space character on the line - let isFirstInListItem st = (orgStateParserContext st == ListItemState) && - (orgStateLastPreCharPos st == Nothing) + let isFirstInListItem st = orgStateParserContext st == ListItemState && + isNothing (orgStateLastPreCharPos st) guard =<< not . isFirstInListItem <$> getState blockAttrs <- blockAttributes lookAhead tableStart @@ -772,7 +591,7 @@ orgToPandocTable :: OrgTable -> Inlines -> Blocks orgToPandocTable (OrgTable colProps heads lns) caption = - let totalWidth = if any (not . isNothing) (map columnRelWidth colProps) + let totalWidth = if any isJust (map columnRelWidth colProps) then Just . sum $ map (fromMaybe 1 . columnRelWidth) colProps else Nothing in B.table caption (map (convertColProp totalWidth) colProps) heads lns @@ -782,7 +601,7 @@ orgToPandocTable (OrgTable colProps heads lns) caption = let align' = fromMaybe AlignDefault $ columnAlignment colProp width' = fromMaybe 0 $ (\w t -> (fromIntegral w / fromIntegral t)) - <$> (columnRelWidth colProp) + <$> columnRelWidth colProp <*> totalWidth in (align', width') @@ -808,7 +627,7 @@ tableAlignRow = try $ do columnPropertyCell :: Monad m => OrgParser m ColumnProperty columnPropertyCell = emptyCell <|> propCell <?> "alignment info" where - emptyCell = ColumnProperty Nothing Nothing <$ (try $ skipSpaces *> endOfCell) + emptyCell = ColumnProperty Nothing Nothing <$ try (skipSpaces *> endOfCell) propCell = try $ ColumnProperty <$> (skipSpaces *> char '<' @@ -854,28 +673,28 @@ normalizeTable (OrgTable colProps heads rows) = rowToContent :: OrgTable -> OrgTableRow -> F OrgTable -rowToContent orgTable row = +rowToContent tbl row = case row of OrgHlineRow -> return singleRowPromotedToHeader OrgAlignRow props -> return . setProperties $ props OrgContentRow cs -> appendToBody cs where singleRowPromotedToHeader :: OrgTable - singleRowPromotedToHeader = case orgTable of - OrgTable{ orgTableHeader = [], orgTableRows = b:[] } -> - orgTable{ orgTableHeader = b , orgTableRows = [] } - _ -> orgTable + singleRowPromotedToHeader = case tbl of + OrgTable{ orgTableHeader = [], orgTableRows = [b] } -> + tbl{ orgTableHeader = b , orgTableRows = [] } + _ -> tbl setProperties :: [ColumnProperty] -> OrgTable - setProperties ps = orgTable{ orgTableColumnProperties = ps } + setProperties ps = tbl{ orgTableColumnProperties = ps } appendToBody :: F [Blocks] -> F OrgTable appendToBody frow = do newRow <- frow - let oldRows = orgTableRows orgTable + let oldRows = orgTableRows tbl -- NOTE: This is an inefficient O(n) operation. This should be changed -- if performance ever becomes a problem. - return orgTable{ orgTableRows = oldRows ++ [newRow] } + return tbl{ orgTableRows = oldRows ++ [newRow] } -- @@ -917,7 +736,7 @@ noteBlock = try $ do paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) paraOrPlain = try $ do -- Make sure we are not looking at a headline - notFollowedBy' (char '*' *> (oneOf " *")) + notFollowedBy' (char '*' *> oneOf " *") ils <- inlines nl <- option False (newline *> return True) -- Read block as paragraph, except if we are in a list context and the block @@ -926,7 +745,7 @@ paraOrPlain = try $ do try (guard nl *> notFollowedBy (inList *> (() <$ orderedListStart <|> bulletListStart)) *> return (B.para <$> ils)) - <|> (return (B.plain <$> ils)) + <|> return (B.plain <$> ils) -- @@ -938,16 +757,16 @@ list = choice [ definitionList, bulletList, orderedList ] <?> "list" definitionList :: PandocMonad m => OrgParser m (F Blocks) definitionList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap B.definitionList . fmap compactifyDL . sequence + fmap (B.definitionList . compactifyDL) . sequence <$> many1 (definitionListItem $ bulletListStart' (Just n)) bulletList :: PandocMonad m => OrgParser m (F Blocks) bulletList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap B.bulletList . fmap compactify . sequence + fmap (B.bulletList . compactify) . sequence <$> many1 (listItem (bulletListStart' $ Just n)) orderedList :: PandocMonad m => OrgParser m (F Blocks) -orderedList = fmap B.orderedList . fmap compactify . sequence +orderedList = fmap (B.orderedList . compactify) . sequence <$> many1 (listItem orderedListStart) bulletListStart' :: Monad m => Maybe Int -> OrgParser m Int @@ -1004,16 +823,3 @@ listContinuation markerLength = try $ <*> many blankline) where listLine = try $ indentWith markerLength *> anyLineNewline - - -- indent by specified number of spaces (or equiv. tabs) - indentWith :: Monad m => Int -> OrgParser m String - indentWith num = do - tabStop <- getOption readerTabStop - if num < tabStop - then count num (char ' ') - else choice [ try (count num (char ' ')) - , try (char '\t' >> count (num - tabStop) (char ' ')) ] - --- | Parse any line, include the final newline in the output. -anyLineNewline :: Monad m => OrgParser m String -anyLineNewline = (++ "\n") <$> anyLine diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs new file mode 100644 index 000000000..743f6cc0e --- /dev/null +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -0,0 +1,304 @@ +{- +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ViewPatterns #-} +{- | + Module : Text.Pandoc.Readers.Org.DocumentTree + Copyright : Copyright (C) 2014-2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +Parsers for org-mode headlines and document subtrees +-} +module Text.Pandoc.Readers.Org.DocumentTree + ( documentTree + , headlineToBlocks + ) where + +import Control.Arrow ((***)) +import Control.Monad (guard, void) +import Data.Char (toLower, toUpper) +import Data.List (intersperse) +import Data.Monoid ((<>)) +import Text.Pandoc.Builder (Blocks, Inlines) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.Readers.Org.BlockStarts +import Text.Pandoc.Readers.Org.ParserState +import Text.Pandoc.Readers.Org.Parsing + +import qualified Data.Map as Map +import qualified Text.Pandoc.Builder as B + +-- +-- Org headers +-- + +-- | Parse input as org document tree. +documentTree :: PandocMonad m + => OrgParser m (F Blocks) + -> OrgParser m (F Inlines) + -> OrgParser m (F Headline) +documentTree blocks inline = do + initialBlocks <- blocks + headlines <- sequence <$> manyTill (headline blocks inline 1) eof + title <- fmap (getTitle . unMeta) . orgStateMeta <$> getState + return $ do + headlines' <- headlines + initialBlocks' <- initialBlocks + title' <- title + return Headline + { headlineLevel = 0 + , headlineTodoMarker = Nothing + , headlineText = B.fromList title' + , headlineTags = mempty + , headlineProperties = mempty + , headlineContents = initialBlocks' + , headlineChildren = headlines' + } + where + getTitle :: Map.Map String MetaValue -> [Inline] + getTitle metamap = + case Map.lookup "title" metamap of + Just (MetaInlines inlns) -> inlns + _ -> [] + +newtype Tag = Tag { fromTag :: String } + deriving (Show, Eq) + +-- | Create a tag containing the given string. +toTag :: String -> Tag +toTag = Tag + +-- | The key (also called name or type) of a property. +newtype PropertyKey = PropertyKey { fromKey :: String } + deriving (Show, Eq, Ord) + +-- | Create a property key containing the given string. Org mode keys are +-- case insensitive and are hence converted to lower case. +toPropertyKey :: String -> PropertyKey +toPropertyKey = PropertyKey . map toLower + +-- | The value assigned to a property. +newtype PropertyValue = PropertyValue { fromValue :: String } + +-- | Create a property value containing the given string. +toPropertyValue :: String -> PropertyValue +toPropertyValue = PropertyValue + +-- | Check whether the property value is non-nil (i.e. truish). +isNonNil :: PropertyValue -> Bool +isNonNil p = map toLower (fromValue p) `notElem` ["()", "{}", "nil"] + +-- | Key/value pairs from a PROPERTIES drawer +type Properties = [(PropertyKey, PropertyValue)] + +-- | Org mode headline (i.e. a document subtree). +data Headline = Headline + { headlineLevel :: Int + , headlineTodoMarker :: Maybe TodoMarker + , headlineText :: Inlines + , headlineTags :: [Tag] + , headlineProperties :: Properties + , headlineContents :: Blocks + , headlineChildren :: [Headline] + } + +-- | Read an Org mode headline and its contents (i.e. a document subtree). +-- @lvl@ gives the minimum acceptable level of the tree. +headline :: PandocMonad m + => OrgParser m (F Blocks) + -> OrgParser m (F Inlines) + -> Int + -> OrgParser m (F Headline) +headline blocks inline lvl = try $ do + level <- headerStart + guard (lvl <= level) + todoKw <- optionMaybe todoKeyword + title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle + tags <- option [] headerTags + newline + properties <- option mempty propertiesDrawer + contents <- blocks + children <- many (headline blocks inline (level + 1)) + return $ do + title' <- title + contents' <- contents + children' <- sequence children + return Headline + { headlineLevel = level + , headlineTodoMarker = todoKw + , headlineText = title' + , headlineTags = tags + , headlineProperties = properties + , headlineContents = contents' + , headlineChildren = children' + } + where + endOfTitle :: Monad m => OrgParser m () + endOfTitle = void . lookAhead $ optional headerTags *> newline + + headerTags :: Monad m => OrgParser m [Tag] + headerTags = try $ + let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':' + in map toTag <$> (skipSpaces *> char ':' *> many1 tag <* skipSpaces) + +-- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks +headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks +headlineToBlocks hdln@Headline {..} = do + maxHeadlineLevels <- getExportSetting exportHeadlineLevels + case () of + _ | any isNoExportTag headlineTags -> return mempty + _ | any isArchiveTag headlineTags -> archivedHeadlineToBlocks hdln + _ | isCommentTitle headlineText -> return mempty + _ | headlineLevel >= maxHeadlineLevels -> headlineToHeaderWithList hdln + _ | otherwise -> headlineToHeaderWithContents hdln + +isNoExportTag :: Tag -> Bool +isNoExportTag = (== toTag "noexport") + +isArchiveTag :: Tag -> Bool +isArchiveTag = (== toTag "ARCHIVE") + +-- | Check if the title starts with COMMENT. +-- FIXME: This accesses builder internals not intended for use in situations +-- like these. Replace once keyword parsing is supported. +isCommentTitle :: Inlines -> Bool +isCommentTitle (B.toList -> (Str "COMMENT":_)) = True +isCommentTitle _ = False + +archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks +archivedHeadlineToBlocks hdln = do + archivedTreesOption <- getExportSetting exportArchivedTrees + case archivedTreesOption of + ArchivedTreesNoExport -> return mempty + ArchivedTreesExport -> headlineToHeaderWithContents hdln + ArchivedTreesHeadlineOnly -> headlineToHeader hdln + +headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks +headlineToHeaderWithList hdln@Headline {..} = do + maxHeadlineLevels <- getExportSetting exportHeadlineLevels + header <- headlineToHeader hdln + listElements <- mapM headlineToBlocks headlineChildren + let listBlock = if null listElements + then mempty + else B.orderedList listElements + let headerText = if maxHeadlineLevels == headlineLevel + then header + else flattenHeader header + return $ headerText <> headlineContents <> listBlock + where + flattenHeader :: Blocks -> Blocks + flattenHeader blks = + case B.toList blks of + (Header _ _ inlns:_) -> B.para (B.fromList inlns) + _ -> mempty + +headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks +headlineToHeaderWithContents hdln@Headline {..} = do + header <- headlineToHeader hdln + childrenBlocks <- mconcat <$> mapM headlineToBlocks headlineChildren + return $ header <> headlineContents <> childrenBlocks + +headlineToHeader :: Monad m => Headline -> OrgParser m Blocks +headlineToHeader Headline {..} = do + exportTodoKeyword <- getExportSetting exportWithTodoKeywords + exportTags <- getExportSetting exportWithTags + let todoText = if exportTodoKeyword + then case headlineTodoMarker of + Just kw -> todoKeywordToInlines kw <> B.space + Nothing -> mempty + else mempty + let text = todoText <> headlineText <> + if exportTags + then tagsToInlines headlineTags + else mempty + let propAttr = propertiesToAttr headlineProperties + attr <- registerHeader propAttr headlineText + return $ B.headerWith attr headlineLevel text + +todoKeyword :: Monad m => OrgParser m TodoMarker +todoKeyword = try $ do + taskStates <- activeTodoMarkers <$> getState + let kwParser tdm = try (tdm <$ string (todoMarkerName tdm) <* spaceChar) + choice (map kwParser taskStates) + +todoKeywordToInlines :: TodoMarker -> Inlines +todoKeywordToInlines tdm = + let todoText = todoMarkerName tdm + todoState = map toLower . show $ todoMarkerState tdm + classes = [todoState, todoText] + in B.spanWith (mempty, classes, mempty) (B.str todoText) + +propertiesToAttr :: Properties -> Attr +propertiesToAttr properties = + let + toStringPair = fromKey *** fromValue + customIdKey = toPropertyKey "custom_id" + classKey = toPropertyKey "class" + unnumberedKey = toPropertyKey "unnumbered" + specialProperties = [customIdKey, classKey, unnumberedKey] + id' = maybe mempty fromValue . lookup customIdKey $ properties + cls = maybe mempty fromValue . lookup classKey $ properties + kvs' = map toStringPair . filter ((`notElem` specialProperties) . fst) + $ properties + isUnnumbered = + maybe False isNonNil . lookup unnumberedKey $ properties + in + (id', words cls ++ ["unnumbered" | isUnnumbered], kvs') + +tagsToInlines :: [Tag] -> Inlines +tagsToInlines [] = mempty +tagsToInlines tags = + (B.space <>) . mconcat . intersperse (B.str "\160") . map tagToInline $ tags + where + tagToInline :: Tag -> Inlines + tagToInline t = tagSpan t . B.smallcaps . B.str $ fromTag t + +-- | Wrap the given inline in a span, marking it as a tag. +tagSpan :: Tag -> Inlines -> Inlines +tagSpan t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) + + + + + +-- | Read a :PROPERTIES: drawer and return the key/value pairs contained +-- within. +propertiesDrawer :: Monad m => OrgParser m Properties +propertiesDrawer = try $ do + drawerType <- drawerStart + guard $ map toUpper drawerType == "PROPERTIES" + manyTill property (try endOfDrawer) + where + property :: Monad m => OrgParser m (PropertyKey, PropertyValue) + property = try $ (,) <$> key <*> value + + key :: Monad m => OrgParser m PropertyKey + key = fmap toPropertyKey . try $ + skipSpaces *> char ':' *> many1Till nonspaceChar (char ':') + + value :: Monad m => OrgParser m PropertyValue + value = fmap toPropertyValue . try $ + skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline) + + endOfDrawer :: Monad m => OrgParser m String + endOfDrawer = try $ + skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 1d6fdd7e1..11f0972d5 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,8 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.ExportSettings + Copyright : © 2016–2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -71,7 +71,7 @@ exportSetting = choice , ignoredSetting "pri" , ignoredSetting "prop" , ignoredSetting "stat" - , ignoredSetting "tags" + , booleanSetting "tags" (\val es -> es { exportWithTags = val }) , ignoredSetting "tasks" , ignoredSetting "tex" , ignoredSetting "timestamp" diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 64ffb8ef5..66273e05d 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,8 +18,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.Inlines + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -48,7 +48,7 @@ import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline) import Text.TeXMath (DisplayType (..), readTeX, writePandoc) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap -import Control.Monad (guard, mplus, mzero, void, when) +import Control.Monad (guard, mplus, mzero, unless, void, when) import Control.Monad.Trans (lift) import Data.Char (isAlphaNum, isSpace) import Data.List (intersperse) @@ -63,7 +63,7 @@ import Prelude hiding (sequence) -- recordAnchorId :: PandocMonad m => String -> OrgParser m () recordAnchorId i = updateState $ \s -> - s{ orgStateAnchorIds = i : (orgStateAnchorIds s) } + s{ orgStateAnchorIds = i : orgStateAnchorIds s } pushToInlineCharStack :: PandocMonad m => Char -> OrgParser m () pushToInlineCharStack c = updateState $ \s -> @@ -120,6 +120,7 @@ inline = , superscript , inlineLaTeX , exportSnippet + , macro , smart , symbol ] <* (guard =<< newlinesCountWithinLimits) @@ -183,7 +184,7 @@ cite = try $ berkeleyCite <|> do , orgRefCite , berkeleyTextualCite ] - return $ (flip B.cite (B.text raw)) <$> cs + return $ flip B.cite (B.text raw) <$> cs -- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@). pandocOrgCite :: PandocMonad m => OrgParser m (F [Citation]) @@ -208,7 +209,7 @@ normalOrgRefCite = try $ do orgRefCiteList :: PandocMonad m => CitationMode -> OrgParser m (F Citation) orgRefCiteList citeMode = try $ do key <- orgRefCiteKey - returnF $ Citation + returnF Citation { citationId = key , citationPrefix = mempty , citationSuffix = mempty @@ -231,11 +232,11 @@ berkeleyCite = try $ do return $ if parens then toCite - . maybe id (\p -> alterFirst (prependPrefix p)) prefix - . maybe id (\s -> alterLast (appendSuffix s)) suffix + . maybe id (alterFirst . prependPrefix) prefix + . maybe id (alterLast . appendSuffix) suffix $ citationList else maybe mempty (<> " ") prefix - <> (toListOfCites $ map toInTextMode citationList) + <> toListOfCites (map toInTextMode citationList) <> maybe mempty (", " <>) suffix where toCite :: [Citation] -> Inlines @@ -249,7 +250,7 @@ berkeleyCite = try $ do alterFirst, alterLast :: (a -> a) -> [a] -> [a] alterFirst _ [] = [] - alterFirst f (c:cs) = (f c):cs + alterFirst f (c:cs) = f c : cs alterLast f = reverse . alterFirst f . reverse prependPrefix, appendSuffix :: Inlines -> Citation -> Citation @@ -270,7 +271,7 @@ berkeleyCitationList = try $ do skipSpaces commonPrefix <- optionMaybe (try $ citationListPart <* char ';') citations <- citeList - commonSuffix <- optionMaybe (try $ citationListPart) + commonSuffix <- optionMaybe (try citationListPart) char ']' return (BerkeleyCitationList parens <$> sequence commonPrefix @@ -338,8 +339,15 @@ linkLikeOrgRefCite = try $ do -- | Read a citation key. The characters allowed in citation keys are taken -- from the `org-ref-cite-re` variable in `org-ref.el`. orgRefCiteKey :: PandocMonad m => OrgParser m String -orgRefCiteKey = try . many1 . satisfy $ \c -> - isAlphaNum c || c `elem` ("-_:\\./"::String) +orgRefCiteKey = + let citeKeySpecialChars = "-_:\\./," :: String + isCiteKeySpecialChar c = c `elem` citeKeySpecialChars + isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c + endOfCitation = try $ do + many $ satisfy isCiteKeySpecialChar + satisfy $ not . isCiteKeyChar + in try $ satisfy isCiteKeyChar `many1Till` lookAhead endOfCitation + -- | Supported citation types. Only a small subset of org-ref types is -- supported for now. TODO: rewrite this, use LaTeX reader as template. @@ -365,15 +373,16 @@ citation = try $ do return $ do x <- pref y <- suff - return $ Citation{ citationId = key - , citationPrefix = B.toList x - , citationSuffix = B.toList y - , citationMode = if suppress_author - then SuppressAuthor - else NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } + return Citation + { citationId = key + , citationPrefix = B.toList x + , citationSuffix = B.toList y + , citationMode = if suppress_author + then SuppressAuthor + else NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } where prefix = trimInlinesF . mconcat <$> manyTill inline (char ']' <|> (']' <$ lookAhead citeKey)) @@ -395,7 +404,7 @@ inlineNote = try $ do ref <- many alphaNum char ':' note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']') - when (not $ null ref) $ + unless (null ref) $ addToNotesTable ("fn:" ++ ref, note) return $ B.note <$> note @@ -405,7 +414,7 @@ referencedNote = try $ do return $ do notes <- asksF orgStateNotes' case lookup ref notes of - Nothing -> return $ B.str $ "[" ++ ref ++ "]" + Nothing -> return . B.str $ "[" ++ ref ++ "]" Just contents -> do st <- askF let contents' = runF contents st{ orgStateNotes' = [] } @@ -429,7 +438,7 @@ explicitOrImageLink = try $ do src <- srcF case cleanLinkString title of Just imgSrc | isImageFilename imgSrc -> - pure $ B.link src "" $ B.image imgSrc mempty mempty + pure . B.link src "" $ B.image imgSrc mempty mempty _ -> linkToInlinesF src =<< title' @@ -686,13 +695,13 @@ mathEnd c = try $ do return res -enclosedInlines :: PandocMonad m => OrgParser m a +enclosedInlines :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m (F Inlines) enclosedInlines start end = try $ trimInlinesF . mconcat <$> enclosed start end inline -enclosedRaw :: PandocMonad m => OrgParser m a +enclosedRaw :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m String enclosedRaw start end = try $ @@ -771,7 +780,7 @@ notAfterForbiddenBorderChar = do -- | Read a sub- or superscript expression subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines) subOrSuperExpr = try $ - choice [ id <$> charsInBalanced '{' '}' (noneOf "\n\r") + choice [ charsInBalanced '{' '}' (noneOf "\n\r") , enclosing ('(', ')') <$> charsInBalanced '(' ')' (noneOf "\n\r") , simpleSubOrSuperString ] >>= parseFromString (mconcat <$> many inline) @@ -809,7 +818,7 @@ inlineLaTeX = try $ do enableExtension Ext_raw_tex (readerExtensions def) } } texMathToPandoc :: String -> Maybe [Inline] - texMathToPandoc cs = (maybeRight $ readTeX cs) >>= writePandoc DisplayInline + texMathToPandoc cs = maybeRight (readTeX cs) >>= writePandoc DisplayInline maybeRight :: Either a b -> Maybe b maybeRight = either (const Nothing) Just @@ -839,26 +848,49 @@ exportSnippet = try $ do snippet <- manyTill anyChar (try $ string "@@") returnF $ B.rawInline format snippet +macro :: PandocMonad m => OrgParser m (F Inlines) +macro = try $ do + recursionDepth <- orgStateMacroDepth <$> getState + guard $ recursionDepth < 15 + string "{{{" + name <- many alphaNum + args <- ([] <$ string "}}}") + <|> char '(' *> argument `sepBy` char ',' <* eoa + expander <- lookupMacro name <$> getState + case expander of + Nothing -> mzero + Just fn -> do + updateState $ \s -> s { orgStateMacroDepth = recursionDepth + 1 } + res <- parseFromString (mconcat <$> many inline) $ fn args + updateState $ \s -> s { orgStateMacroDepth = recursionDepth } + return res + where + argument = many $ notFollowedBy eoa *> noneOf "," + eoa = string ")}}}" + smart :: PandocMonad m => OrgParser m (F Inlines) -smart = do - guardEnabled Ext_smart - doubleQuoted <|> singleQuoted <|> - choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses]) +smart = choice [doubleQuoted, singleQuoted, orgApostrophe, orgDash, orgEllipses] where orgDash = do - guard =<< getExportSetting exportSpecialStrings - dash <* updatePositions '-' + guardOrSmartEnabled =<< getExportSetting exportSpecialStrings + pure <$> dash <* updatePositions '-' orgEllipses = do - guard =<< getExportSetting exportSpecialStrings - ellipses <* updatePositions '.' - orgApostrophe = - (char '\'' <|> char '\8217') <* updateLastPreCharPos - <* updateLastForbiddenCharPos - *> return (B.str "\x2019") + guardOrSmartEnabled =<< getExportSetting exportSpecialStrings + pure <$> ellipses <* updatePositions '.' + orgApostrophe = do + guardEnabled Ext_smart + (char '\'' <|> char '\8217') <* updateLastPreCharPos + <* updateLastForbiddenCharPos + returnF (B.str "\x2019") + +guardOrSmartEnabled :: PandocMonad m => Bool -> OrgParser m () +guardOrSmartEnabled b = do + smartExtension <- extensionEnabled Ext_smart <$> getOption readerExtensions + guard (b || smartExtension) singleQuoted :: PandocMonad m => OrgParser m (F Inlines) singleQuoted = try $ do - guard =<< getExportSetting exportSmartQuotes + guardOrSmartEnabled =<< getExportSetting exportSmartQuotes singleQuoteStart updatePositions '\'' withQuoteContext InSingleQuote $ @@ -870,10 +902,13 @@ singleQuoted = try $ do -- in the same paragraph. doubleQuoted :: PandocMonad m => OrgParser m (F Inlines) doubleQuoted = try $ do - guard =<< getExportSetting exportSmartQuotes + guardOrSmartEnabled =<< getExportSetting exportSmartQuotes doubleQuoteStart updatePositions '"' contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) - (withQuoteContext InDoubleQuote $ (doubleQuoteEnd <* updateLastForbiddenCharPos) >> return - (fmap B.doubleQuoted . trimInlinesF $ contents)) - <|> (return $ return (B.str "\8220") <> contents) + let doubleQuotedContent = withQuoteContext InDoubleQuote $ do + doubleQuoteEnd + updateLastForbiddenCharPos + return . fmap B.doubleQuoted . trimInlinesF $ contents + let leftQuoteAndContent = return $ pure (B.str "\8220") <> contents + doubleQuotedContent <|> leftQuoteAndContent diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 7938fc6c6..d22902eae 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -44,7 +44,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition -import Control.Monad (mzero, void) +import Control.Monad (mzero, void, when) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M @@ -75,14 +75,16 @@ declarationLine :: PandocMonad m => OrgParser m () declarationLine = try $ do key <- map toLower <$> metaKey (key', value) <- metaValue key - updateState $ \st -> st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } + let addMetaValue st = + st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } + when (key' /= "results") $ updateState addMetaValue metaKey :: Monad m => OrgParser m String metaKey = map toLower <$> many1 (noneOf ": \n\r") <* char ':' <* skipSpaces -metaValue :: PandocMonad m => String -> OrgParser m (String, (F MetaValue)) +metaValue :: PandocMonad m => String -> OrgParser m (String, F MetaValue) metaValue key = let inclKey = "header-includes" in case key of @@ -109,7 +111,7 @@ metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline metaInlinesCommaSeparated :: PandocMonad m => OrgParser m (F MetaValue) metaInlinesCommaSeparated = do - itemStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',') + itemStrs <- many1 (noneOf ",\n") `sepBy1` char ',' newline items <- mapM (parseFromString inlinesTillNewline . (++ "\n")) itemStrs let toMetaInlines = MetaInlines . B.toList @@ -151,6 +153,7 @@ optionLine = try $ do "todo" -> todoSequence >>= updateState . registerTodoSequence "seq_todo" -> todoSequence >>= updateState . registerTodoSequence "typ_todo" -> todoSequence >>= updateState . registerTodoSequence + "macro" -> macroDefinition >>= updateState . registerMacro _ -> mzero addLinkFormat :: Monad m => String @@ -160,7 +163,7 @@ addLinkFormat key formatter = updateState $ \s -> let fs = orgStateLinkFormatters s in s{ orgStateLinkFormatters = M.insert key formatter fs } -parseLinkFormat :: Monad m => OrgParser m ((String, String -> String)) +parseLinkFormat :: Monad m => OrgParser m (String, String -> String) parseLinkFormat = try $ do linkType <- (:) <$> letter <*> many (alphaNum <|> oneOf "-_") <* skipSpaces linkSubst <- parseFormat @@ -169,8 +172,7 @@ parseLinkFormat = try $ do -- | An ad-hoc, single-argument-only implementation of a printf-style format -- parser. parseFormat :: Monad m => OrgParser m (String -> String) -parseFormat = try $ do - replacePlain <|> replaceUrl <|> justAppend +parseFormat = try $ replacePlain <|> replaceUrl <|> justAppend where -- inefficient, but who cares replacePlain = try $ (\x -> concat . flip intersperse x) @@ -218,3 +220,27 @@ todoSequence = try $ do let todoMarkers = map (TodoMarker Todo) todo doneMarkers = map (TodoMarker Done) done in todoMarkers ++ doneMarkers + +macroDefinition :: Monad m => OrgParser m (String, [String] -> String) +macroDefinition = try $ do + macroName <- many1 nonspaceChar <* skipSpaces + firstPart <- expansionPart + (elemOrder, parts) <- unzip <$> many ((,) <$> placeholder <*> expansionPart) + let expander = mconcat . alternate (firstPart:parts) . reorder elemOrder + return (macroName, expander) + where + placeholder :: Monad m => OrgParser m Int + placeholder = try . fmap read $ char '$' *> many1 digit + + expansionPart :: Monad m => OrgParser m String + expansionPart = try $ many (notFollowedBy placeholder *> noneOf "\n\r") + + alternate :: [a] -> [a] -> [a] + alternate [] ys = ys + alternate xs [] = xs + alternate (x:xs) (y:ys) = x : y : alternate xs ys + + reorder :: [Int] -> [String] -> [String] + reorder perm xs = + let element n = take 1 $ drop (n - 1) xs + in concatMap element perm diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index bdd1dc951..92f868516 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -1,8 +1,7 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -20,8 +19,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.ParserState + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -39,6 +38,9 @@ module Text.Pandoc.Readers.Org.ParserState , TodoState (..) , activeTodoMarkers , registerTodoSequence + , MacroExpander + , lookupMacro + , registerMacro , F , askF , asksF @@ -58,14 +60,14 @@ import qualified Data.Set as Set import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Definition (Meta (..), nullMeta) -import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Logging -import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..), - HasLogMessages (..), - HasLastStrPosition (..), HasQuoteContext (..), +import Text.Pandoc.Options (ReaderOptions (..)) +import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..), + HasIncludeFiles (..), HasLastStrPosition (..), + HasLogMessages (..), HasQuoteContext (..), HasReaderOptions (..), ParserContext (..), - QuoteContext (..), SourcePos, Future, - askF, asksF, returnF, runF, trimInlinesF) + QuoteContext (..), SourcePos, askF, asksF, returnF, + runF, trimInlinesF) -- | This is used to delay evaluation until all relevant information has been -- parsed and made available in the parser state. @@ -78,6 +80,8 @@ type OrgNoteTable = [OrgNoteRecord] -- | Map of functions for link transformations. The map key is refers to the -- link-type, the corresponding function transforms the given link string. type OrgLinkFormatters = M.Map String (String -> String) +-- | Macro expander function +type MacroExpander = [String] -> String -- | The states in which a todo item can be data TodoState = Todo | Done @@ -101,10 +105,13 @@ data OrgParserState = OrgParserState , orgStateExportSettings :: ExportSettings , orgStateHeaderMap :: M.Map Inlines String , orgStateIdentifiers :: Set.Set String + , orgStateIncludeFiles :: [String] , orgStateLastForbiddenCharPos :: Maybe SourcePos , orgStateLastPreCharPos :: Maybe SourcePos , orgStateLastStrPos :: Maybe SourcePos , orgStateLinkFormatters :: OrgLinkFormatters + , orgStateMacros :: M.Map String MacroExpander + , orgStateMacroDepth :: Int , orgStateMeta :: F Meta , orgStateNotes' :: OrgNoteTable , orgStateOptions :: ReaderOptions @@ -141,6 +148,12 @@ instance HasLogMessages OrgParserState where addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st } getLogMessages st = reverse $ orgLogMessages st +instance HasIncludeFiles OrgParserState where + getIncludeFiles = orgStateIncludeFiles + addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st } + dropLatestIncludeFile st = + st { orgStateIncludeFiles = drop 1 $ orgStateIncludeFiles st } + instance Default OrgParserState where def = defaultOrgParserState @@ -152,10 +165,13 @@ defaultOrgParserState = OrgParserState , orgStateExportSettings = def , orgStateHeaderMap = M.empty , orgStateIdentifiers = Set.empty + , orgStateIncludeFiles = [] , orgStateLastForbiddenCharPos = Nothing , orgStateLastPreCharPos = Nothing , orgStateLastStrPos = Nothing , orgStateLinkFormatters = M.empty + , orgStateMacros = M.empty + , orgStateMacroDepth = 0 , orgStateMeta = return nullMeta , orgStateNotes' = [] , orgStateOptions = def @@ -185,6 +201,15 @@ activeTodoSequences st = activeTodoMarkers :: OrgParserState -> TodoSequence activeTodoMarkers = concat . activeTodoSequences +lookupMacro :: String -> OrgParserState -> Maybe MacroExpander +lookupMacro macroName = M.lookup macroName . orgStateMacros + +registerMacro :: (String, MacroExpander) -> OrgParserState -> OrgParserState +registerMacro (name, expander) st = + let curMacros = orgStateMacros st + in st{ orgStateMacros = M.insert name expander curMacros } + + -- -- Export Settings @@ -213,6 +238,7 @@ data ExportSettings = ExportSettings , exportWithAuthor :: Bool -- ^ Include author in final meta-data , exportWithCreator :: Bool -- ^ Include creator in final meta-data , exportWithEmail :: Bool -- ^ Include email in final meta-data + , exportWithTags :: Bool -- ^ Keep tags as part of headlines , exportWithTodoKeywords :: Bool -- ^ Keep TODO keywords in headers } @@ -225,11 +251,12 @@ defaultExportSettings = ExportSettings , exportDrawers = Left ["LOGBOOK"] , exportEmphasizedText = True , exportHeadlineLevels = 3 - , exportSmartQuotes = True + , exportSmartQuotes = False , exportSpecialStrings = True , exportSubSuperscripts = True , exportWithAuthor = True , exportWithCreator = True , exportWithEmail = True + , exportWithTags = True , exportWithTodoKeywords = True } diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 464ef9ca6..3273c92e4 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,8 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.Parsing + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -31,6 +31,8 @@ functions are adapted to Org-mode specific functionality. module Text.Pandoc.Readers.Org.Parsing ( OrgParser , anyLine + , anyLineNewline + , indentWith , blanklines , newline , parseFromString @@ -70,6 +72,8 @@ module Text.Pandoc.Readers.Org.Parsing , dash , ellipses , citeKey + , gridTableWith + , insertIncludedFileF -- * Re-exports from Text.Pandoc.Parsec , runParser , runParserT diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index f89ce6732..952082ec1 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,8 +18,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.Shared + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -56,7 +56,7 @@ cleanLinkString s = '.':'/':_ -> Just s -- relative path '.':'.':'/':_ -> Just s -- relative path -- Relative path or URL (file schema) - 'f':'i':'l':'e':':':s' -> Just $ if ("//" `isPrefixOf` s') then s else s' + 'f':'i':'l':'e':':':s' -> Just $ if "//" `isPrefixOf` s' then s else s' _ | isUrl s -> Just s -- URL _ -> Nothing where diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 7564998ff..fb5f6f2d4 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.RST - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -32,10 +32,11 @@ Conversion from reStructuredText to 'Pandoc' document. -} module Text.Pandoc.Readers.RST ( readRST ) where import Control.Monad (guard, liftM, mzero, when) +import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (throwError) import Data.Char (isHexDigit, isSpace, toLower, toUpper) -import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf, - nub, sort, transpose, union) +import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, + isSuffixOf, nub, sort, transpose, union) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) import Data.Monoid ((<>)) @@ -52,20 +53,22 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared import Text.Printf (printf) +import Data.Text (Text) +import qualified Data.Text as T -- TODO: -- [ ] .. parsed-literal -- [ ] :widths: attribute in .. table -- [ ] .. csv-table --- [ ] .. list-table -- | Parse reStructuredText string and return Pandoc document. readRST :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readRST opts s = do - parsed <- (readWithM parseRST) def{ stateOptions = opts } (s ++ "\n\n") + parsed <- (readWithM parseRST) def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e @@ -131,7 +134,10 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds adjustAuthors (Meta metamap) = Meta $ M.adjust splitAuthors "author" $ M.adjust toPlain "date" $ M.adjust toPlain "title" - $ M.mapKeys (\k -> if k == "authors" then "author" else k) + $ M.mapKeys (\k -> + if k == "authors" + then "author" + else k) $ metamap toPlain (MetaBlocks [Para xs]) = MetaInlines xs toPlain x = x @@ -193,7 +199,7 @@ parseRST = do parseCitation :: PandocMonad m => (String, String) -> RSTParser m (Inlines, [Blocks]) parseCitation (ref, raw) = do - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw return $ (B.spanWith (ref, ["citation-label"], []) (B.str ref), [contents]) @@ -243,7 +249,7 @@ fieldListItem :: PandocMonad m => Int -> RSTParser m (Inlines, [Blocks]) fieldListItem minIndent = try $ do (name, raw) <- rawFieldListItem minIndent term <- parseInlineFromString name - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw optional blanklines return (term, [contents]) @@ -442,7 +448,7 @@ blockQuote :: PandocMonad m => RSTParser m Blocks blockQuote = do raw <- indentedBlock -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ raw ++ "\n\n" + contents <- parseFromString' parseBlocks $ raw ++ "\n\n" return $ B.blockQuote contents {- @@ -530,7 +536,7 @@ definitionListItem = try $ do term <- trimInlines . mconcat <$> many1Till inline endline raw <- indentedBlock -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ raw ++ "\n" + contents <- parseFromString' parseBlocks $ raw ++ "\n" return (term, [contents]) definitionList :: PandocMonad m => RSTParser m Blocks @@ -558,26 +564,16 @@ listLine :: Monad m => Int -> RSTParser m [Char] listLine markerLength = try $ do notFollowedBy blankline indentWith markerLength - line <- anyLine - return $ line ++ "\n" - --- indent by specified number of spaces (or equiv. tabs) -indentWith :: Monad m => Int -> RSTParser m [Char] -indentWith num = do - tabStop <- getOption readerTabStop - if (num < tabStop) - then count num (char ' ') - else choice [ try (count num (char ' ')), - (try (char '\t' >> count (num - tabStop) (char ' '))) ] + anyLineNewline -- parse raw text for one list item, excluding start marker and continuations rawListItem :: Monad m => RSTParser m Int -> RSTParser m (Int, [Char]) rawListItem start = try $ do markerLength <- start - firstLine <- anyLine + firstLine <- anyLineNewline restLines <- many (listLine markerLength) - return (markerLength, (firstLine ++ "\n" ++ (concat restLines))) + return (markerLength, firstLine ++ concat restLines) -- continuation of a list item - indented and separated by blankline or -- (in compact lists) endline. @@ -602,13 +598,17 @@ listItem start = try $ do let oldContext = stateParserContext state setState $ state {stateParserContext = ListItemState} -- parse the extracted block, which may itself contain block elements - parsed <- parseFromString parseBlocks $ concat (first:rest) ++ "\n" + parsed <- parseFromString' parseBlocks $ concat (first:rest) ++ "\n" updateState (\st -> st {stateParserContext = oldContext}) return $ case B.toList parsed of - [Para xs] -> B.singleton $ Plain xs - [Para xs, BulletList ys] -> B.fromList [Plain xs, BulletList ys] - [Para xs, OrderedList s ys] -> B.fromList [Plain xs, OrderedList s ys] - [Para xs, DefinitionList ys] -> B.fromList [Plain xs, DefinitionList ys] + [Para xs] -> + B.singleton $ Plain xs + [Para xs, BulletList ys] -> + B.fromList [Plain xs, BulletList ys] + [Para xs, OrderedList s ys] -> + B.fromList [Plain xs, OrderedList s ys] + [Para xs, DefinitionList ys] -> + B.fromList [Plain xs, DefinitionList ys] _ -> parsed orderedList :: PandocMonad m => RSTParser m Blocks @@ -685,22 +685,23 @@ directive' = do (lengthToDim . filter (not . isSpace)) case label of "table" -> tableDirective top fields body' + "list-table" -> listTableDirective top fields body' "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields - "container" -> parseFromString parseBlocks body' + "container" -> parseFromString' parseBlocks body' "replace" -> B.para <$> -- consumed by substKey parseInlineFromString (trim top) "unicode" -> B.para <$> -- consumed by substKey parseInlineFromString (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' + "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 <$> parseInlineFromString top _ | label `elem` ["attention","caution","danger","error","hint", "important","note","tip","warning","admonition"] -> - do bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + do bod <- parseFromString' parseBlocks $ top ++ "\n\n" ++ body' let lab = case label of "admonition" -> mempty (l:ls) -> B.divWith ("",["admonition-title"],[]) @@ -713,11 +714,11 @@ directive' = do (trim top ++ if null subtit then "" else (": " ++ subtit)) - bod <- parseFromString parseBlocks body' + bod <- parseFromString' parseBlocks body' return $ B.divWith ("",["sidebar"],[]) $ tit <> bod "topic" -> do tit <- B.para . B.strong <$> parseInlineFromString top - bod <- parseFromString parseBlocks body' + bod <- parseFromString' parseBlocks body' return $ B.divWith ("",["topic"],[]) $ tit <> bod "default-role" -> mempty <$ updateState (\s -> s { stateRstDefaultRole = @@ -733,9 +734,10 @@ directive' = do "math" -> return $ B.para $ mconcat $ map B.displayMath $ toChunks $ top ++ "\n\n" ++ body "figure" -> do - (caption, legend) <- parseFromString extractCaption body' + (caption, legend) <- parseFromString' extractCaption body' let src = escapeURI $ trim top - return $ B.para (B.imageWith (imgAttr "figclass") src "fig:" caption) <> legend + return $ B.para (B.imageWith (imgAttr "figclass") src "fig:" + caption) <> legend "image" -> do let src = escapeURI $ trim top let alt = B.str $ maybe "image" trim $ lookup "alt" fields @@ -746,38 +748,74 @@ directive' = do $ B.imageWith attr src "" alt Nothing -> B.imageWith attr src "" alt "class" -> do - let attrs = ("", (splitBy isSpace $ trim top), map (\(k,v) -> (k, trimr v)) fields) + let attrs = ("", (splitBy isSpace $ trim top), + map (\(k,v) -> (k, trimr v)) fields) -- directive content or the first immediately following element children <- case body of "" -> block - _ -> parseFromString parseBlocks body' + _ -> parseFromString' parseBlocks body' return $ B.divWith attrs children other -> do pos <- getPosition logMessage $ SkippedContent (".. " ++ other) pos - bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + bod <- parseFromString' parseBlocks $ top ++ "\n\n" ++ body' return $ B.divWith ("",[other],[]) bod tableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks tableDirective top _fields body = do - bs <- parseFromString parseBlocks body + bs <- parseFromString' parseBlocks body case B.toList bs of [Table _ aligns' widths' header' rows'] -> do - title <- parseFromString (trimInlines . mconcat <$> many inline) top + title <- parseFromString' (trimInlines . mconcat <$> many inline) top -- TODO widths -- align is not applicable since we can't represent whole table align return $ B.singleton $ Table (B.toList title) aligns' widths' header' rows' _ -> return mempty + +-- TODO: :stub-columns:. +-- Only the first row becomes the header even if header-rows: > 1, +-- since Pandoc doesn't support a table with multiple header rows. +-- We don't need to parse :align: as it represents the whole table align. +listTableDirective :: PandocMonad m + => String -> [(String, String)] -> String + -> RSTParser m Blocks +listTableDirective top fields body = do + bs <- parseFromString' parseBlocks body + title <- parseFromString' (trimInlines . mconcat <$> many inline) top + let rows = takeRows $ B.toList bs + headerRowsNum = fromMaybe (0 :: Int) $ + lookup "header-rows" fields >>= safeRead + (headerRow,bodyRows,numOfCols) = case rows of + x:xs -> if headerRowsNum > 0 + then (x, xs, length x) + else ([], rows, length x) + _ -> ([],[],0) + widths = case trim <$> lookup "widths" fields of + Just "auto" -> replicate numOfCols 0 + Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ + splitBy (`elem` (" ," :: String)) specs + _ -> replicate numOfCols 0 + return $ B.table title + (zip (replicate numOfCols AlignDefault) widths) + headerRow + bodyRows + where takeRows [BulletList rows] = map takeCells rows + takeRows _ = [] + takeCells [BulletList cells] = map B.fromList cells + takeCells _ = [] + normWidths ws = map (/ max 1 (sum ws)) ws + -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix -addNewRole :: PandocMonad m => String -> [(String, String)] -> RSTParser m Blocks +addNewRole :: PandocMonad m + => String -> [(String, String)] -> RSTParser m Blocks addNewRole roleString fields = do pos <- getPosition - (role, parentRole) <- parseFromString inheritedRole roleString + (role, parentRole) <- parseFromString' inheritedRole roleString customRoles <- stateRstCustomRoles <$> getState let getBaseRole (r, f, a) roles = case M.lookup r roles of @@ -804,7 +842,8 @@ addNewRole roleString fields = do SkippedContent ":format: [because parent of role is not :raw:]" pos _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos when (parentRole == "raw" && countKeys "format" > 1) $ - logMessage $ SkippedContent ":format: [after first in definition of role]" + logMessage $ SkippedContent + ":format: [after first in definition of role]" pos when (parentRole == "code" && countKeys "language" > 1) $ logMessage $ SkippedContent @@ -819,7 +858,8 @@ addNewRole roleString fields = do where countKeys k = length . filter (== k) . map fst $ fields inheritedRole = - (,) <$> roleName <*> ((char '(' *> roleName <* char ')') <|> pure "span") + (,) <$> roleName <*> ((char '(' *> roleName <* char ')') + <|> pure "span") -- Can contain character codes as decimal numbers or @@ -996,7 +1036,8 @@ substKey = try $ do [Para ils] -> return $ B.fromList ils _ -> mzero let key = toKey $ stripFirstAndLast ref - updateState $ \s -> s{ stateSubstitutions = M.insert key il $ stateSubstitutions s } + updateState $ \s -> s{ stateSubstitutions = + M.insert key il $ stateSubstitutions s } anonymousKey :: Monad m => RSTParser m () anonymousKey = try $ do @@ -1005,7 +1046,8 @@ anonymousKey = try $ do pos <- getPosition let key = toKey $ "_" ++ printf "%09d" (sourceLine pos) --TODO: parse width, height, class and name attributes - updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } + updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ + stateKeys s } stripTicks :: String -> String stripTicks = reverse . stripTick . reverse . stripTick @@ -1020,7 +1062,8 @@ regularKey = try $ do src <- targetURI let key = toKey $ stripTicks ref --TODO: parse width, height, class and name attributes - updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } + updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ + stateKeys s } headerBlock :: PandocMonad m => RSTParser m [Char] headerBlock = do @@ -1087,7 +1130,7 @@ simpleTableRow indices = do let cols = map unlines . transpose $ firstLine : conLines ++ [replicate (length indices) "" | not (null conLines)] - mapM (parseFromString parseBlocks) cols + mapM (parseFromString' parseBlocks) cols simpleTableSplitLine :: [Int] -> String -> [String] simpleTableSplitLine indices line = @@ -1110,7 +1153,7 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM (parseFromString (mconcat <$> many plain)) $ + heads <- mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads return (heads, aligns, indices) @@ -1119,8 +1162,12 @@ simpleTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks simpleTable headless = do - tbl <- tableWith (simpleTableHeader headless) simpleTableRow - sep simpleTableFooter + let wrapIdFst (a, b, c) = (Identity a, b, c) + wrapId = fmap Identity + tbl <- runIdentity <$> tableWith + (wrapIdFst <$> simpleTableHeader headless) + (wrapId <$> simpleTableRow) + sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) case B.toList tbl of [Table c a _w h l] -> return $ B.singleton $ @@ -1134,7 +1181,8 @@ simpleTable headless = do gridTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks -gridTable headerless = gridTableWith parseBlocks headerless +gridTable headerless = runIdentity <$> + gridTableWith (Identity <$> parseBlocks) headerless table :: PandocMonad m => RSTParser m Blocks table = gridTable False <|> simpleTable False <|> @@ -1161,7 +1209,7 @@ inline = choice [ note -- can start with whitespace, so try before ws , symbol ] <?> "inline" parseInlineFromString :: PandocMonad m => String -> RSTParser m Inlines -parseInlineFromString = parseFromString (trimInlines . mconcat <$> many inline) +parseInlineFromString = parseFromString' (trimInlines . mconcat <$> many inline) hyphens :: Monad m => RSTParser m Inlines hyphens = do @@ -1220,7 +1268,8 @@ interpretedRole = try $ do (role, contents) <- roleBefore <|> roleAfter renderRole contents Nothing role nullAttr -renderRole :: PandocMonad m => String -> Maybe String -> String -> Attr -> RSTParser m Inlines +renderRole :: PandocMonad m + => String -> Maybe String -> String -> Attr -> RSTParser m Inlines renderRole contents fmt role attr = case role of "sup" -> return $ B.superscript $ B.str contents "superscript" -> return $ B.superscript $ B.str contents @@ -1353,7 +1402,8 @@ referenceLink = try $ do (k:_) -> return k ((src,tit), attr) <- lookupKey [] key -- if anonymous link, remove key so it won't be used again - when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable } + when (isAnonKey key) $ updateState $ \s -> + s{ stateKeys = M.delete key keyTable } return $ B.linkWith attr src tit label' -- We keep a list of oldkeys so we can detect lookup loops. @@ -1423,7 +1473,7 @@ note = try $ do -- Note references inside other notes are allowed in reST, but -- not yet in this implementation. updateState $ \st -> st{ stateNotes = [] } - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw let newnotes = if (ref == "*" || ref == "#") -- auto-numbered -- delete the note so the next auto-numbered note -- doesn't get the same contents: diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index ecb609ae9..9e544c4ac 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -49,14 +49,17 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, macro, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Text.Pandoc.XML (fromEntities) +import Data.Text (Text) +import qualified Data.Text as T -- | Read twiki from an input string and return a Pandoc document. readTWiki :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc readTWiki opts s = do - res <- readWithM parseTWiki def{ stateOptions = opts } (s ++ "\n\n") + res <- readWithM parseTWiki def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case res of Left e -> throwError e Right d -> return d @@ -106,7 +109,7 @@ parseHtmlContentWithAttrs tag parser = do parsedContent <- try $ parseContent content return (attr, parsedContent) where - parseContent = parseFromString $ nested $ manyTill parser endOfContent + parseContent = parseFromString' $ nested $ manyTill parser endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof parseHtmlContent :: PandocMonad m => String -> TWParser m a -> TWParser m [a] @@ -233,7 +236,7 @@ listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat filterSpaces = reverse . dropWhile (== ' ') . reverse listContinuation = notFollowedBy (string prefix >> marker) >> string " " >> lineContent - parseContent = parseFromString $ many1 $ nestedList <|> parseInline + parseContent = parseFromString' $ many1 $ nestedList <|> parseInline parseInline = many1Till inline (lastNewline <|> newlineBeforeNestedList) >>= return . B.plain . mconcat nestedList = list prefix @@ -297,7 +300,7 @@ noautolink = do setState $ st{ stateAllowLinks = True } return $ mconcat blocks where - parseContent = parseFromString $ many $ block + parseContent = parseFromString' $ many $ block para :: PandocMonad m => TWParser m B.Blocks para = many1Till inline endOfParaElement >>= return . result . mconcat @@ -349,13 +352,13 @@ linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) where lastNewline = eof >> return mempty innerNewline = return B.space -between :: (Monoid c, PandocMonad m) +between :: (Monoid c, PandocMonad m, Show b) => TWParser m a -> TWParser m b -> (TWParser m b -> TWParser m c) -> TWParser m c between start end p = mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end) -enclosed :: (Monoid b, PandocMonad m) +enclosed :: (Monoid b, PandocMonad m, Show a) => TWParser m a -> (TWParser m a -> TWParser m b) -> TWParser m b enclosed sep p = between sep (try $ sep <* endMarker) p where @@ -525,4 +528,4 @@ linkText = do return (url, "", content) where linkContent = (char '[') >> many1Till anyChar (char ']') >>= parseLinkContent - parseLinkContent = parseFromString $ many1 inline + parseLinkContent = parseFromString' $ many1 inline diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 047aa061c..1669e3e51 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -1,6 +1,6 @@ {- -Copyright (C) 2010-2015 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@' - and John MacFarlane +Copyright (C) 2010-2012 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@' + 2010-2017 John MacFarlane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Textile - Copyright : Copyright (C) 2010-2015 Paul Rivier and John MacFarlane + Copyright : Copyright (C) 2010-2012 Paul Rivier + 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Paul Rivier <paul*rivier#demotera*com> @@ -69,14 +70,17 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (trim) +import Data.Text (Text) +import qualified Data.Text as T -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readTextile opts s = do - parsed <- readWithM parseTextile def{ stateOptions = opts } (s ++ "\n\n") + parsed <- readWithM parseTextile def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e @@ -314,7 +318,7 @@ definitionListItem = try $ do optional whitespace >> newline s <- many1Till anyChar (try (string "=:" >> newline)) -- this ++ "\n\n" does not look very good - ds <- parseFromString parseBlocks (s ++ "\n\n") + ds <- parseFromString' parseBlocks (s ++ "\n\n") return [ds] -- raw content @@ -366,7 +370,7 @@ tableCell = try $ do notFollowedBy blankline raw <- trim <$> many (noneOf "|\n" <|> try (char '\n' <* notFollowedBy blankline)) - content <- mconcat <$> parseFromString (many inline) raw + content <- mconcat <$> parseFromString' (many inline) raw return ((isHeader, alignment), B.plain content) -- | A table row is made of many table cells @@ -388,7 +392,7 @@ table = try $ do _ <- attributes char '.' rawcapt <- trim <$> anyLine - parseFromString (mconcat <$> many inline) rawcapt + parseFromString' (mconcat <$> many inline) rawcapt rawrows <- many1 $ (skipMany ignorableRow) >> tableRow skipMany ignorableRow blanklines @@ -506,7 +510,7 @@ note = try $ do notes <- stateNotes <$> getState case lookup ref notes of Nothing -> fail "note not found" - Just raw -> B.note <$> parseFromString parseBlocks raw + Just raw -> B.note <$> parseFromString' parseBlocks raw -- | Special chars markupChars :: [Char] @@ -585,8 +589,9 @@ link = try $ do char ':' let stop = if bracketed then char ']' - else lookAhead $ space <|> - try (oneOf "!.,;:" *> (space <|> newline)) + else lookAhead $ space <|> eof' <|> + try (oneOf "!.,;:" *> + (space <|> newline <|> eof')) url <- many1Till nonspaceChar stop let name' = if B.toList name == [Str "$"] then B.str url else name return $ if attr == nullAttr @@ -690,7 +695,7 @@ langAttr = do return $ \(id',classes,keyvals) -> (id',classes,("lang",lang):keyvals) -- | Parses material surrounded by a parser. -surrounded :: PandocMonad m +surrounded :: (PandocMonad m, Show t) => ParserT [Char] st m t -- ^ surrounding parser -> ParserT [Char] st m a -- ^ content parser (to be used repeatedly) -> ParserT [Char] st m [a] @@ -727,3 +732,5 @@ groupedInlineMarkup = try $ do singleton :: a -> [a] singleton x = [x] +eof' :: Monad m => ParserT [Char] s m Char +eof' = '\n' <$ eof diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 33f785109..260bb7fff 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -42,11 +42,11 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (macro, space, spaces, uri) import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI) ---import Network.URI (isURI) -- Not sure whether to use this function import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default - +import Data.Text (Text) +import qualified Data.Text as T import Control.Monad.Except (catchError, throwError) import Data.Time.Format (formatTime) import Text.Pandoc.Class (PandocMonad) @@ -91,11 +91,11 @@ getT2TMeta = do -- | Read Txt2Tags from an input string returning a Pandoc document readTxt2Tags :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc readTxt2Tags opts s = do meta <- getT2TMeta - let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n") + let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (T.unpack s ++ "\n\n") case parsed of Right result -> return $ result Left e -> throwError e @@ -213,7 +213,7 @@ quote :: T2T Blocks quote = try $ do lookAhead tab rawQuote <- many1 (tab *> optional spaces *> anyLine) - contents <- parseFromString parseBlocks (intercalate "\n" rawQuote ++ "\n\n") + contents <- parseFromString' parseBlocks (intercalate "\n" rawQuote ++ "\n\n") return $ B.blockQuote contents commentLine :: T2T Inlines @@ -265,7 +265,7 @@ listItem start end = try $ do firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- concat <$> many (listContinuation markerLength) - parseFromString end $ firstLine ++ blank ++ rest + parseFromString' end $ firstLine ++ blank ++ rest -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. @@ -277,12 +277,6 @@ listContinuation markerLength = try $ <*> many blankline) where listLine = try $ indentWith markerLength *> anyLineNewline -anyLineNewline :: T2T String -anyLineNewline = (++ "\n") <$> anyLine - -indentWith :: Int -> T2T String -indentWith n = count n space - -- Table table :: T2T Blocks @@ -446,7 +440,7 @@ inlineMarkup p f c special = try $ do Just middle -> do lastChar <- anyChar end <- many1 (char c) - let parser inp = parseFromString (mconcat <$> many p) inp + let parser inp = parseFromString' (mconcat <$> many p) inp let start' = case drop 2 start of "" -> mempty xs -> special xs diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 53cb4a4b5..55df147b6 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 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 : Text.Pandoc.SelfContained - Copyright : Copyright (C) 2011-2016 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,13 +35,14 @@ import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) +import Data.Monoid ((<>)) import Data.ByteString (ByteString) import Data.ByteString.Base64 import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import Data.Char (isAlphaNum, isAscii, toLower) import Data.List (isPrefixOf) -import Network.URI (URI (..), escapeURIString, isURI, parseURI) +import Network.URI (URI (..), escapeURIString, parseURI) import System.FilePath (takeDirectory, takeExtension, (</>)) import Text.HTML.TagSoup import Text.Pandoc.Class (PandocMonad (..), fetchItem, report) @@ -49,7 +50,7 @@ import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (WriterOptions (..)) -import Text.Pandoc.Shared (renderTags', trim) +import Text.Pandoc.Shared (isURI, renderTags', trim) import Text.Pandoc.UTF8 (toString) import Text.Parsec (ParsecT, runParserT) import qualified Text.Parsec as P @@ -95,9 +96,9 @@ convertTags sourceURL (t@(TagOpen "script" as):TagClose "script":ts) = (("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) : TagClose "script" : rest Right (mime, bs) - | (mime == "text/javascript" || - mime == "application/javascript" || - mime == "application/x-javascript") && + | ("text/javascript" `isPrefixOf` mime || + "application/javascript" `isPrefixOf` mime || + "application/x-javascript" `isPrefixOf` mime) && not ("</script" `B.isInfixOf` bs) -> return $ TagOpen "script" [("type", typeAttr)|not (null typeAttr)] @@ -121,11 +122,12 @@ convertTags sourceURL (t@(TagOpen "link" as):ts) = (("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) : rest Right (mime, bs) - | mime == "text/css" && not ("</" `B.isInfixOf` bs) -> do + | "text/css" `isPrefixOf` mime + && not ("</" `B.isInfixOf` bs) -> do rest <- convertTags sourceURL $ dropWhile (==TagClose "link") ts return $ - TagOpen "style" [("type", "text/css")] + TagOpen "style" [("type", mime)] : TagText (toString bs) : TagClose "style" : rest @@ -149,7 +151,21 @@ cssURLs sourceURL d orig = do parseCSSUrls :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString parseCSSUrls sourceURL d = B.concat <$> P.many - (pCSSWhite <|> pCSSComment <|> pCSSUrl sourceURL d <|> pCSSOther) + (pCSSWhite <|> pCSSComment <|> pCSSImport sourceURL d <|> + pCSSUrl sourceURL d <|> pCSSOther) + +pCSSImport :: PandocMonad m => Maybe String -> FilePath + -> ParsecT ByteString () m ByteString +pCSSImport sourceURL d = P.try $ do + P.string "@import" + P.spaces + res <- (pQuoted <|> pUrl) >>= handleCSSUrl sourceURL d + P.spaces + P.char ';' + P.spaces + case res of + Left b -> return $ B.pack "@import " <> b + Right (_, b) -> return b -- Note: some whitespace in CSS is significant, so we can't collapse it! pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString @@ -170,6 +186,24 @@ pCSSOther = do pCSSUrl :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString pCSSUrl sourceURL d = P.try $ do + res <- pUrl >>= handleCSSUrl sourceURL d + case res of + Left b -> return b + Right (mt,b) -> do + let enc = makeDataURI (mt, b) + return (B.pack $ "url(" ++ enc ++ ")") + +pQuoted :: PandocMonad m + => ParsecT ByteString () m (String, ByteString) +pQuoted = P.try $ do + quote <- P.oneOf "\"'" + url <- P.manyTill P.anyChar (P.char quote) + let fallback = B.pack ([quote] ++ trim url ++ [quote]) + return (url, fallback) + +pUrl :: PandocMonad m + => ParsecT ByteString () m (String, ByteString) +pUrl = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") @@ -178,12 +212,29 @@ pCSSUrl sourceURL d = P.try $ do P.char ')' let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ maybe "" (:[]) quote ++ ")") - case trim url of - '#':_ -> return fallback - 'd':'a':'t':'a':':':_ -> return fallback + return (url, fallback) + +handleCSSUrl :: PandocMonad m + => Maybe String -> FilePath -> (String, ByteString) + -> ParsecT ByteString () m + (Either ByteString (MimeType, ByteString)) +handleCSSUrl sourceURL d (url, fallback) = do + -- pipes are used in URLs provided by Google Code fonts + -- but parseURI doesn't like them, so we escape them: + case escapeURIString (/='|') (trim url) of + '#':_ -> return $ Left fallback + 'd':'a':'t':'a':':':_ -> return $ Left fallback u -> do let url' = if isURI u then u else d </> u - enc <- lift $ getDataURI sourceURL "" url' - return (B.pack $ "url(" ++ enc ++ ")") + res <- lift $ getData sourceURL "" url' + case res of + Left uri -> return $ Left (B.pack $ "url(" ++ uri ++ ")") + Right (mt, raw) -> do + -- note that the downloaded CSS may + -- itself contain url(...). + b <- if "text/css" `isPrefixOf` mt + then cssURLs sourceURL d raw + else return raw + return $ Right (mt, b) getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String getDataURI sourceURL mimetype src = do @@ -215,7 +266,7 @@ getData sourceURL mimetype src = do uriQuery = "", uriFragment = "" } _ -> Nothing - result <- if mime == "text/css" + result <- if "text/css" `isPrefixOf` mime then cssURLs cssSourceURL (takeDirectory src) raw' else return raw' return $ Right (mime, result) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 8256d14c0..745e809d0 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -2,7 +2,7 @@ FlexibleContexts, ScopedTypeVariables, PatternGuards, ViewPatterns #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Shared - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -70,6 +70,7 @@ module Text.Pandoc.Shared ( isTightList, addMetaField, makeMeta, + eastAsianLineBreakFilter, -- * TagSoup HTML handling renderTags', -- * File handling @@ -81,6 +82,9 @@ module Text.Pandoc.Shared ( openURL, collapseFilePath, filteredFilesFromArchive, + -- * URI handling + schemes, + isURI, -- * Error handling mapLeft, -- * for squashing blocks @@ -104,7 +108,7 @@ import Data.List ( find, stripPrefix, intercalate ) import Data.Maybe (mapMaybe) import Data.Version ( showVersion ) import qualified Data.Map as M -import Network.URI ( escapeURIString, unEscapeString ) +import Network.URI ( URI(uriScheme), escapeURIString, unEscapeString, parseURI ) import qualified Data.Set as Set import System.Directory import System.FilePath (splitDirectories, isPathSeparator) @@ -117,6 +121,7 @@ import qualified Control.Monad.State as S import qualified Control.Exception as E import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) +import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Compat.Time import Data.Time.Clock.POSIX import System.IO.Error @@ -128,7 +133,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import Data.ByteString.Base64 (decodeLenient) import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr) -import qualified Data.Text as T (toUpper, pack, unpack) +import qualified Data.Text as T import Data.ByteString.Lazy (toChunks, fromChunks) import qualified Data.ByteString.Lazy as BL import Paths_pandoc (version) @@ -140,9 +145,9 @@ import Text.Pandoc.Data (dataFiles) #else import Paths_pandoc (getDataFileName) #endif -#ifdef HTTP_CLIENT import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, - Request(port,host,requestHeaders)) + Request(port,host,requestHeaders), + HttpException) import Network.HTTP.Client (parseRequest) import Network.HTTP.Client (newManager) import Network.HTTP.Client.Internal (addProxy) @@ -150,12 +155,6 @@ import Network.HTTP.Client.TLS (tlsManagerSettings) import System.Environment (getEnv) import Network.HTTP.Types.Header ( hContentType, hUserAgent) import Network (withSocketsDo) -#else -import Network.URI (parseURI) -import Network.HTTP (findHeader, rspBody, - RequestMethod(..), HeaderName(..), mkRequest) -import Network.Browser (browse, setAllowRedirects, setOutHandler, request) -#endif -- | Version number of pandoc library. pandocVersion :: String @@ -280,26 +279,20 @@ escapeURI = escapeURIString (not . needsEscaping) where needsEscaping c = isSpace c || c `elem` ['<','>','|','"','{','}','[',']','^', '`'] - -- | Convert tabs to spaces and filter out DOS line endings. -- Tabs will be preserved if tab stop is set to 0. tabFilter :: Int -- ^ Tab stop - -> String -- ^ Input - -> String -tabFilter tabStop = - let go _ [] = "" - go _ ('\n':xs) = '\n' : go tabStop xs - go _ ('\r':'\n':xs) = '\n' : go tabStop xs - go _ ('\r':xs) = '\n' : go tabStop xs - go spsToNextStop ('\t':xs) = - if tabStop == 0 - then '\t' : go tabStop xs - else replicate spsToNextStop ' ' ++ go tabStop xs - go 1 (x:xs) = - x : go tabStop xs - go spsToNextStop (x:xs) = - x : go (spsToNextStop - 1) xs - in go tabStop + -> T.Text -- ^ Input + -> T.Text +tabFilter tabStop = T.filter (/= '\r') . T.unlines . + (if tabStop == 0 then id else map go) . T.lines + where go s = + let (s1, s2) = T.break (== '\t') s + in if T.null s2 + then s1 + else s1 <> T.replicate + (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ") + <> go (T.drop 1 s2) -- -- Date/time @@ -581,6 +574,16 @@ makeMeta title authors date = $ addMetaField "date" (B.fromList date) $ nullMeta +-- | Remove soft breaks between East Asian characters. +eastAsianLineBreakFilter :: Pandoc -> Pandoc +eastAsianLineBreakFilter = bottomUp go + where go (x:SoftBreak:y:zs) = + case (stringify x, stringify y) of + (xs@(_:_), (c:_)) + | charWidth (last xs) == 2 && charWidth c == 2 -> x:y:zs + _ -> x:SoftBreak:y:zs + go xs = xs + -- -- TagSoup HTML handling -- @@ -709,14 +712,13 @@ readDataFileUTF8 userDir fname = UTF8.toString `fmap` readDataFile userDir fname -- | Read from a URL and return raw data and maybe mime type. -openURL :: String -> IO (BS.ByteString, Maybe MimeType) +openURL :: String -> IO (Either HttpException (BS.ByteString, Maybe MimeType)) openURL u | Just u'' <- stripPrefix "data:" u = let mime = takeWhile (/=',') u'' contents = B8.pack $ unEscapeString $ drop 1 $ dropWhile (/=',') u'' - in return (decodeLenient contents, Just mime) -#ifdef HTTP_CLIENT - | otherwise = withSocketsDo $ do + in return $ Right (decodeLenient contents, Just mime) + | otherwise = E.try $ withSocketsDo $ do let parseReq = parseRequest (proxy :: Either IOError String) <- tryIOError $ getEnv "http_proxy" @@ -738,19 +740,6 @@ openURL u resp <- newManager tlsManagerSettings >>= httpLbs req'' return (BS.concat $ toChunks $ responseBody resp, UTF8.toString `fmap` lookup hContentType (responseHeaders resp)) -#else - | otherwise = getBodyAndMimeType `fmap` browse - (do liftIO $ UTF8.hPutStrLn stderr $ "Fetching " ++ u ++ "..." - setOutHandler $ const (return ()) - setAllowRedirects True - request (getRequest' u')) - where getBodyAndMimeType (_, r) = (rspBody r, findHeader HdrContentType r) - getRequest' uriString = case parseURI uriString of - Nothing -> error ("Not a valid URL: " ++ - uriString) - Just v -> mkRequest GET v - u' = escapeURIString (/= '|') u -- pipes are rejected by Network.URI -#endif -- -- Error reporting @@ -794,6 +783,71 @@ filteredFilesFromArchive zf f = fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString) fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e) + +-- +-- IANA URIs +-- + +-- | Schemes from http://www.iana.org/assignments/uri-schemes.html plus +-- the unofficial schemes doi, javascript, isbn, pmid. +schemes :: Set.Set String +schemes = Set.fromList + -- Official IANA schemes + [ "aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs" + , "aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin" + , "blob", "bolo", "browserext", "callto", "cap", "chrome", "chrome-extension" + , "cid", "coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs" + , "data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle" + , "dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed" + , "feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg" + , "git", "gizmoproject", "go", "gopher", "graph", "gtalk", "h323", "ham" + , "hcp", "http", "https", "hxxp", "hxxps", "hydrazone", "iax", "icap", "icon" + , "im", "imap", "info", "iotdisco", "ipn", "ipp", "ipps", "irc", "irc6" + , "ircs", "iris", "iris.beep", "iris.lwz", "iris.xpc", "iris.xpcs" + , "isostore", "itms", "jabber", "jar", "jms", "keyparc", "lastfm", "ldap" + , "ldaps", "lvlt", "magnet", "mailserver", "mailto", "maps", "market" + , "message", "mid", "mms", "modem", "mongodb", "moz", "ms-access" + , "ms-browser-extension", "ms-drive-to", "ms-enrollment", "ms-excel" + , "ms-gamebarservices", "ms-getoffice", "ms-help", "ms-infopath" + , "ms-media-stream-id", "ms-officeapp", "ms-project", "ms-powerpoint" + , "ms-publisher", "ms-search-repair", "ms-secondary-screen-controller" + , "ms-secondary-screen-setup", "ms-settings", "ms-settings-airplanemode" + , "ms-settings-bluetooth", "ms-settings-camera", "ms-settings-cellular" + , "ms-settings-cloudstorage", "ms-settings-connectabledevices" + , "ms-settings-displays-topology", "ms-settings-emailandaccounts" + , "ms-settings-language", "ms-settings-location", "ms-settings-lock" + , "ms-settings-nfctransactions", "ms-settings-notifications" + , "ms-settings-power", "ms-settings-privacy", "ms-settings-proximity" + , "ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace" + , "ms-spd", "ms-sttoverlay", "ms-transit-to", "ms-virtualtouchpad" + , "ms-visio", "ms-walk-to", "ms-whiteboard", "ms-whiteboard-cmd", "ms-word" + , "msnim", "msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs" + , "ni", "nih", "nntp", "notes", "ocf", "oid", "onenote", "onenote-cmd" + , "opaquelocktoken", "pack", "palm", "paparazzi", "pkcs11", "platform", "pop" + , "pres", "prospero", "proxy", "pwid", "psyc", "qb", "query", "redis" + , "rediss", "reload", "res", "resource", "rmi", "rsync", "rtmfp", "rtmp" + , "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp", "sgn" + , "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp", "snews" + , "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh", "steam" + , "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel", "teliaeid" + , "telnet", "tftp", "things", "thismessage", "tip", "tn3270", "tool", "turn" + , "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event", "vemmi" + , "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal", "wpid" + , "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire" + , "xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r" + , "z39.50s" + -- Inofficial schemes + , "doi", "isbn", "javascript", "pmid" + ] + +-- | Check if the string is a valid URL with a IANA or frequently used but +-- unofficial scheme (see @schemes@). +isURI :: String -> Bool +isURI = maybe False hasKnownScheme . parseURI + where + hasKnownScheme = (`Set.member` schemes) . map toLower . + filter (/= ':') . uriScheme + --- --- Squash blocks into inlines --- diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index b53e0eb6d..cd7695dbe 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Slides - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 26aeb9a73..9b635a97b 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -3,7 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} {- -Copyright (C) 2009-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2009-2017 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Templates - Copyright : Copyright (C) 2009-2016 John MacFarlane + Copyright : Copyright (C) 2009-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index d88a44948..663f30d92 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -1,5 +1,6 @@ +{-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 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 @@ -18,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UTF8 - Copyright : Copyright (C) 2010-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -28,16 +29,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA UTF-8 aware string IO functions that will work with GHC 6.10, 6.12, or 7. -} module Text.Pandoc.UTF8 ( readFile - , writeFile , getContents + , writeFileWith + , writeFile + , putStrWith , putStr + , putStrLnWith , putStrLn + , hPutStrWith , hPutStr + , hPutStrLnWith , hPutStrLn , hGetContents , toString + , toText , fromString + , fromText , toStringLazy + , fromTextLazy + , toTextLazy , fromStringLazy , encodePath , decodeArg @@ -46,7 +56,7 @@ module Text.Pandoc.UTF8 ( readFile where import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as TL @@ -61,23 +71,43 @@ readFile f = do h <- openFile (encodePath f) ReadMode hGetContents h -writeFile :: FilePath -> String -> IO () -writeFile f s = withFile (encodePath f) WriteMode $ \h -> hPutStr h s - getContents :: IO String getContents = hGetContents stdin +writeFileWith :: Newline -> FilePath -> String -> IO () +writeFileWith eol f s = + withFile (encodePath f) WriteMode $ \h -> hPutStrWith eol h s + +writeFile :: FilePath -> String -> IO () +writeFile = writeFileWith nativeNewline + +putStrWith :: Newline -> String -> IO () +putStrWith eol s = hPutStrWith eol stdout s + putStr :: String -> IO () -putStr s = hPutStr stdout s +putStr = putStrWith nativeNewline + +putStrLnWith :: Newline -> String -> IO () +putStrLnWith eol s = hPutStrLnWith eol stdout s putStrLn :: String -> IO () -putStrLn s = hPutStrLn stdout s +putStrLn = putStrLnWith nativeNewline + +hPutStrWith :: Newline -> Handle -> String -> IO () +hPutStrWith eol h s = + hSetNewlineMode h (NewlineMode eol eol) >> + hSetEncoding h utf8 >> IO.hPutStr h s hPutStr :: Handle -> String -> IO () -hPutStr h s = hSetEncoding h utf8 >> IO.hPutStr h s +hPutStr = hPutStrWith nativeNewline + +hPutStrLnWith :: Newline -> Handle -> String -> IO () +hPutStrLnWith eol h s = + hSetNewlineMode h (NewlineMode eol eol) >> + hSetEncoding h utf8 >> IO.hPutStrLn h s hPutStrLn :: Handle -> String -> IO () -hPutStrLn h s = hSetEncoding h utf8 >> IO.hPutStrLn h s +hPutStrLn = hPutStrLnWith nativeNewline hGetContents :: Handle -> IO String hGetContents = fmap toString . B.hGetContents @@ -85,34 +115,47 @@ hGetContents = fmap toString . B.hGetContents -- >> hSetNewlineMode h universalNewlineMode -- >> IO.hGetContents h --- | Drop BOM (byte order marker) if present at beginning of string. --- Note that Data.Text converts the BOM to code point FEFF, zero-width --- no-break space, so if the string begins with this we strip it off. -dropBOM :: String -> String -dropBOM ('\xFEFF':xs) = xs -dropBOM xs = xs - -filterCRs :: String -> String -filterCRs ('\r':'\n':xs) = '\n': filterCRs xs -filterCRs ('\r':xs) = '\n' : filterCRs xs -filterCRs (x:xs) = x : filterCRs xs -filterCRs [] = [] +-- | Convert UTF8-encoded ByteString to Text, also +-- removing '\r' characters. +toText :: B.ByteString -> T.Text +toText = T.decodeUtf8 . filterCRs . dropBOM + where dropBOM bs = + if "\xEF\xBB\xBF" `B.isPrefixOf` bs + then B.drop 3 bs + else bs + filterCRs = B.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\r' characters. toString :: B.ByteString -> String -toString = filterCRs . dropBOM . T.unpack . T.decodeUtf8 +toString = T.unpack . toText -fromString :: String -> B.ByteString -fromString = T.encodeUtf8 . T.pack +-- | Convert UTF8-encoded ByteString to Text, also +-- removing '\r' characters. +toTextLazy :: BL.ByteString -> TL.Text +toTextLazy = TL.decodeUtf8 . filterCRs . dropBOM + where dropBOM bs = + if "\xEF\xBB\xBF" `BL.isPrefixOf` bs + then BL.drop 3 bs + else bs + filterCRs = BL.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\r' characters. toStringLazy :: BL.ByteString -> String -toStringLazy = filterCRs . dropBOM . TL.unpack . TL.decodeUtf8 +toStringLazy = TL.unpack . toTextLazy + +fromText :: T.Text -> B.ByteString +fromText = T.encodeUtf8 + +fromTextLazy :: TL.Text -> BL.ByteString +fromTextLazy = TL.encodeUtf8 + +fromString :: String -> B.ByteString +fromString = fromText . T.pack fromStringLazy :: String -> BL.ByteString -fromStringLazy = TL.encodeUtf8 . TL.pack +fromStringLazy = fromTextLazy . TL.pack encodePath :: FilePath -> FilePath encodePath = id diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 9446c4692..989dd20c6 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UUID - Copyright : Copyright (C) 2010-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 0181f41c9..dbe55449f 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -83,6 +83,7 @@ module Text.Pandoc.Writers import Data.Aeson import Data.List (intercalate) +import Data.Text (Text) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Options @@ -120,59 +121,59 @@ import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL -data Writer m = StringWriter (WriterOptions -> Pandoc -> m String) +data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text) | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) -- | Association list of formats and writers. writers :: PandocMonad m => [ ( String, Writer m) ] writers = [ - ("native" , StringWriter writeNative) - ,("json" , StringWriter $ \o d -> return $ writeJSON o d) + ("native" , TextWriter writeNative) + ,("json" , TextWriter $ \o d -> return $ writeJSON o d) ,("docx" , ByteStringWriter writeDocx) ,("odt" , ByteStringWriter writeODT) ,("epub" , ByteStringWriter writeEPUB3) ,("epub2" , ByteStringWriter writeEPUB2) ,("epub3" , ByteStringWriter writeEPUB3) - ,("fb2" , StringWriter writeFB2) - ,("html" , StringWriter writeHtml5String) - ,("html4" , StringWriter writeHtml4String) - ,("html5" , StringWriter writeHtml5String) - ,("icml" , StringWriter writeICML) - ,("s5" , StringWriter writeS5) - ,("slidy" , StringWriter writeSlidy) - ,("slideous" , StringWriter writeSlideous) - ,("dzslides" , StringWriter writeDZSlides) - ,("revealjs" , StringWriter writeRevealJs) - ,("docbook" , StringWriter writeDocbook5) - ,("docbook4" , StringWriter writeDocbook4) - ,("docbook5" , StringWriter writeDocbook5) - ,("jats" , StringWriter writeJATS) - ,("opml" , StringWriter writeOPML) - ,("opendocument" , StringWriter writeOpenDocument) - ,("latex" , StringWriter writeLaTeX) - ,("beamer" , StringWriter writeBeamer) - ,("context" , StringWriter writeConTeXt) - ,("texinfo" , StringWriter writeTexinfo) - ,("man" , StringWriter writeMan) - ,("ms" , StringWriter writeMs) - ,("markdown" , StringWriter writeMarkdown) - ,("markdown_strict" , StringWriter writeMarkdown) - ,("markdown_phpextra" , StringWriter writeMarkdown) - ,("markdown_github" , StringWriter writeMarkdown) - ,("markdown_mmd" , StringWriter writeMarkdown) - ,("plain" , StringWriter writePlain) - ,("rst" , StringWriter writeRST) - ,("mediawiki" , StringWriter writeMediaWiki) - ,("dokuwiki" , StringWriter writeDokuWiki) - ,("zimwiki" , StringWriter writeZimWiki) - ,("textile" , StringWriter writeTextile) - ,("rtf" , StringWriter writeRTF) - ,("org" , StringWriter writeOrg) - ,("asciidoc" , StringWriter writeAsciiDoc) - ,("haddock" , StringWriter writeHaddock) - ,("commonmark" , StringWriter writeCommonMark) - ,("tei" , StringWriter writeTEI) - ,("muse" , StringWriter writeMuse) + ,("fb2" , TextWriter writeFB2) + ,("html" , TextWriter writeHtml5String) + ,("html4" , TextWriter writeHtml4String) + ,("html5" , TextWriter writeHtml5String) + ,("icml" , TextWriter writeICML) + ,("s5" , TextWriter writeS5) + ,("slidy" , TextWriter writeSlidy) + ,("slideous" , TextWriter writeSlideous) + ,("dzslides" , TextWriter writeDZSlides) + ,("revealjs" , TextWriter writeRevealJs) + ,("docbook" , TextWriter writeDocbook5) + ,("docbook4" , TextWriter writeDocbook4) + ,("docbook5" , TextWriter writeDocbook5) + ,("jats" , TextWriter writeJATS) + ,("opml" , TextWriter writeOPML) + ,("opendocument" , TextWriter writeOpenDocument) + ,("latex" , TextWriter writeLaTeX) + ,("beamer" , TextWriter writeBeamer) + ,("context" , TextWriter writeConTeXt) + ,("texinfo" , TextWriter writeTexinfo) + ,("man" , TextWriter writeMan) + ,("ms" , TextWriter writeMs) + ,("markdown" , TextWriter writeMarkdown) + ,("markdown_strict" , TextWriter writeMarkdown) + ,("markdown_phpextra" , TextWriter writeMarkdown) + ,("markdown_github" , TextWriter writeMarkdown) + ,("markdown_mmd" , TextWriter writeMarkdown) + ,("plain" , TextWriter writePlain) + ,("rst" , TextWriter writeRST) + ,("mediawiki" , TextWriter writeMediaWiki) + ,("dokuwiki" , TextWriter writeDokuWiki) + ,("zimwiki" , TextWriter writeZimWiki) + ,("textile" , TextWriter writeTextile) + ,("rtf" , TextWriter writeRTF) + ,("org" , TextWriter writeOrg) + ,("asciidoc" , TextWriter writeAsciiDoc) + ,("haddock" , TextWriter writeHaddock) + ,("commonmark" , TextWriter writeCommonMark) + ,("tei" , TextWriter writeTEI) + ,("muse" , TextWriter writeMuse) ] getWriter :: PandocMonad m => String -> Either String (Writer m) @@ -182,12 +183,12 @@ getWriter s Right (writerName, setExts) -> case lookup writerName writers of Nothing -> Left $ "Unknown writer: " ++ writerName - Just (StringWriter r) -> Right $ StringWriter $ + Just (TextWriter r) -> Right $ TextWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } Just (ByteStringWriter r) -> Right $ ByteStringWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } -writeJSON :: WriterOptions -> Pandoc -> String -writeJSON _ = UTF8.toStringLazy . encode +writeJSON :: WriterOptions -> Pandoc -> Text +writeJSON _ = UTF8.toText . BL.toStrict . encode diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 20fa7c209..46dbe6eaf 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 : Text.Pandoc.Writers.AsciiDoc - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -43,6 +43,7 @@ import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse, stripPrefix) import qualified Data.Map as M import Data.Maybe (fromMaybe) +import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -62,7 +63,7 @@ data WriterState = WriterState { defListMarker :: String } -- | Convert Pandoc to AsciiDoc. -writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeAsciiDoc opts document = evalStateT (pandocToAsciiDoc opts document) WriterState{ defListMarker = "::" @@ -74,16 +75,18 @@ writeAsciiDoc opts document = type ADW = StateT WriterState -- | Return asciidoc representation of document. -pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m String +pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m Text pandocToAsciiDoc opts (Pandoc meta blocks) = do let titleblock = not $ null (docTitle meta) && null (docAuthors meta) && null (docDate meta) let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToAsciiDoc opts) - (fmap (render colwidth) . inlineListToAsciiDoc opts) + (fmap render' . blockListToAsciiDoc opts) + (fmap render' . inlineListToAsciiDoc opts) meta let addTitleLine (String t) = String $ t <> "\n" <> T.replicate (T.length t) "=" diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 5e0a06bf0..ed316ced9 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMark import Control.Monad.State (State, get, modify, runState) import Data.Foldable (foldrM) +import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition @@ -45,7 +46,7 @@ import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Shared -- | Convert Pandoc to CommonMark. -writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCommonMark opts (Pandoc meta blocks) = do let (blocks', notes) = runState (walkM processNotes blocks) [] notes' = if null notes @@ -71,7 +72,7 @@ processNotes x = return x node :: NodeType -> [Node] -> Node node = Node Nothing -blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m String +blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m Text blocksToCommonMark opts bs = do let cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] colwidth = if writerWrapText opts == WrapAuto @@ -79,14 +80,12 @@ blocksToCommonMark opts bs = do else Nothing nodes <- blocksToNodes bs return $ - T.unpack $ nodeToCommonmark cmarkOpts colwidth $ node DOCUMENT nodes -inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m String +inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m Text inlinesToCommonMark opts ils = return $ - T.unpack $ nodeToCommonmark cmarkOpts colwidth - $ node PARAGRAPH (inlinesToNodes ils) + nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -139,7 +138,7 @@ blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns Para term : concat xs blockToNodes t@(Table _ _ _ _ _) ns = do s <- writeHtml5String def $! Pandoc nullMeta [t] - return (node (HTML_BLOCK (T.pack $! s)) [] : ns) + return (node (HTML_BLOCK s) [] : ns) blockToNodes Null ns = return ns inlinesToNodes :: [Inline] -> [Node] diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 57f920259..2da6a7f9a 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 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 : Text.Pandoc.Writers.ConTeXt - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -33,7 +33,8 @@ import Control.Monad.State import Data.Char (ord) import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) -import Network.URI (isURI, unEscapeString) +import Data.Text (Text) +import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -56,7 +57,7 @@ orderedListStyles :: [Char] orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. -writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeConTeXt options document = let defaultWriterState = WriterState { stNextRef = 1 , stOrderedListLevel = 0 @@ -66,17 +67,19 @@ writeConTeXt options document = type WM = StateT WriterState -pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m String +pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m Text pandocToConTeXt options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToConTeXt) - (fmap (render colwidth) . inlineListToConTeXt) + (fmap render' . blockListToConTeXt) + (fmap render' . inlineListToConTeXt) meta body <- mapM (elementToConTeXt options) $ hierarchicalize blocks - let main = (render colwidth . vcat) body + let main = (render' . vcat) body let layoutFromMargins = intercalate [','] $ catMaybes $ map (\(x,y) -> ((x ++ "=") ++) <$> getField y metadata) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index ce90e4834..1314ef844 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -6,7 +6,7 @@ #else {-# LANGUAGE OverlappingInstances #-} #endif -{- Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> +{- Copyright (C) 2012-2017 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 @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Custom - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -41,6 +41,7 @@ import Control.Monad (when) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M +import Data.Text (Text, pack) import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) @@ -116,7 +117,7 @@ data PandocLuaException = PandocLuaException String instance Exception PandocLuaException -- | Convert Pandoc to custom markup. -writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO String +writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO Text writeCustom luaFile opts doc@(Pandoc meta _) = do luaScript <- UTF8.readFile luaFile enc <- getForeignEncoding @@ -139,8 +140,9 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do setForeignEncoding enc let body = rendered case writerTemplate opts of - Nothing -> return body - Just tpl -> return $ renderTemplate' tpl $ setField "body" body context + Nothing -> return $ pack body + Just tpl -> return $ pack $ + renderTemplate' tpl $ setField "body" body context docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String docToCustom lua opts (Pandoc (Meta metamap) blocks) = do diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index dce2cbd3e..02ffbf831 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -32,6 +32,7 @@ Conversion of 'Pandoc' documents to Docbook XML. module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Control.Monad.Reader import Data.Char (toLower) +import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isPrefixOf, isSuffixOf, stripPrefix) import Data.Monoid (Any (..)) @@ -81,22 +82,23 @@ authorToDocbook opts name' = do in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) -writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocbook4 opts d = runReaderT (writeDocbook opts d) DocBook4 -writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocbook5 opts d = runReaderT (writeDocbook opts d) DocBook5 -- | Convert Pandoc document to string in Docbook format. -writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m String +writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text writeDocbook opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && TopLevelDefault == writerTopLevelDivision opts) @@ -111,10 +113,10 @@ writeDocbook opts (Pandoc meta blocks) = do auths' <- mapM (authorToDocbook opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . + (fmap (render' . vcat) . (mapM (elementToDocbook opts' startLvl) . hierarchicalize)) - (fmap (render colwidth) . inlinesToDocbook opts') + (fmap render' . inlinesToDocbook opts') meta' main <- (render' . vcat) <$> (mapM (elementToDocbook opts' startLvl) elements) let context = defField "body" main diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index fddec91cc..63bb8a5ae 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docx - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -55,7 +55,6 @@ import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Compat.Time import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Generic import Text.Pandoc.Highlighting (highlight) import Text.Pandoc.ImageSize @@ -497,6 +496,11 @@ writeDocx opts doc@(Pandoc meta _) = do , qName (elName e) == "abstractNum" ] ++ [Elem e | e <- allElts , qName (elName e) == "num" ] } + + let keywords = case lookupMeta "keywords" meta of + Just (MetaList xs) -> map stringify xs + _ -> [] + let docPropsPath = "docProps/core.xml" let docProps = mknode "cp:coreProperties" [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties") @@ -506,6 +510,7 @@ writeDocx opts doc@(Pandoc meta _) = do ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")] $ mknode "dc:title" [] (stringify $ docTitle meta) : mknode "dc:creator" [] (intercalate "; " (map stringify $ docAuthors meta)) + : mknode "cp:keywords" [] (intercalate ", " keywords) : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) @@ -876,7 +881,7 @@ blockToOpenXML' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = do let prop = pCustomStyle $ if null alt then "Figure" - else "FigureWithCaption" + else "CaptionedFigure" paraProps <- local (\env -> env { envParaProperties = prop : envParaProperties env }) (getParaProps False) contents <- inlinesToOpenXML opts [Image attr alt (src,tit)] captionNode <- withParaProp (pCustomStyle "ImageCaption") @@ -954,7 +959,7 @@ blockToOpenXML' opts (Table caption aligns widths headers rows) = do caption' ++ [mknode "w:tbl" [] ( mknode "w:tblPr" [] - ( mknode "w:tblStyle" [("w:val","TableNormal")] () : + ( mknode "w:tblStyle" [("w:val","Table")] () : mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () : mknode "w:tblLook" [("w:firstRow","1") | hasHeader ] () : [ mknode "w:tblCaption" [("w:val", captionStr)] () @@ -1060,13 +1065,24 @@ withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withParaPropM = (. flip withParaProp) . (>>=) formattedString :: PandocMonad m => String -> WS m [Element] -formattedString str = do - props <- getTextProps +formattedString str = + -- properly handle soft hyphens + case splitBy (=='\173') str of + [w] -> formattedString' w + ws -> do + sh <- formattedRun [mknode "w:softHyphen" [] ()] + (intercalate sh) <$> mapM formattedString' ws + +formattedString' :: PandocMonad m => String -> WS m [Element] +formattedString' str = do inDel <- asks envInDel - return [ mknode "w:r" [] $ - props ++ - [ mknode (if inDel then "w:delText" else "w:t") - [("xml:space","preserve")] (stripInvalidChars str) ] ] + formattedRun [ mknode (if inDel then "w:delText" else "w:t") + [("xml:space","preserve")] (stripInvalidChars str) ] + +formattedRun :: PandocMonad m => [Element] -> WS m [Element] +formattedRun els = do + props <- getTextProps + return [ mknode "w:r" [] $ props ++ els ] setFirstPara :: PandocMonad m => WS m () setFirstPara = modify $ \s -> s { stFirstPara = True } @@ -1076,7 +1092,8 @@ inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] -inlineToOpenXML' _ (Str str) = formattedString str +inlineToOpenXML' _ (Str str) = + formattedString str inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do @@ -1303,12 +1320,10 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do M.insert src (ident, imgpath, mbMimeType, imgElt, img) $ stImages st } return [imgElt]) - (\e -> do case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') - -- emit alt text - inlinesToOpenXML opts alt) + (\e -> do + report $ CouldNotFetchResource src (show e) + -- emit alt text + inlinesToOpenXML opts alt) br :: Element br = breakElement "textWrapping" diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 5e29acbaf..551a1b0b5 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.DokuWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Clare Macrae <clare.macrae@googlemail.com> @@ -44,13 +44,13 @@ import Control.Monad.Reader (ReaderT, ask, local, runReaderT) import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) -import Network.URI (isURI) +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) -import Text.Pandoc.Shared (camelCaseToHyphenated, escapeURI, linesToPara, +import Text.Pandoc.Shared (camelCaseToHyphenated, escapeURI, isURI, linesToPara, removeFormatting, substitute, trimr) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) @@ -76,7 +76,7 @@ instance Default WriterEnvironment where type DokuWiki m = ReaderT WriterEnvironment (StateT WriterState m) -- | Convert Pandoc to DokuWiki. -writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDokuWiki opts document = runDokuWiki (pandocToDokuWiki opts document) @@ -85,7 +85,7 @@ runDokuWiki = flip evalStateT def . flip runReaderT def -- | Return DokuWiki representation of document. pandocToDokuWiki :: PandocMonad m - => WriterOptions -> Pandoc -> DokuWiki m String + => WriterOptions -> Pandoc -> DokuWiki m Text pandocToDokuWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToDokuWiki opts) @@ -97,7 +97,7 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do then "" -- TODO Was "\n<references />" Check whether I can really remove this: -- if it is definitely to do with footnotes, can remove this whole bit else "" - let main = body ++ notes + let main = pack $ body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ metadata diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 5b64564ce..d68283007 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2010-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.EPUB - Copyright : Copyright (C) 2010-2015 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -40,6 +40,7 @@ import Control.Monad.State (State, StateT, evalState, evalStateT, get, gets, lift, modify, put) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 +import qualified Data.Text.Lazy as TL import Data.Char (isAlphaNum, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M @@ -373,8 +374,8 @@ pandocToEPUB :: PandocMonad m -> E m B.ByteString pandocToEPUB version opts doc@(Pandoc meta _) = do let epub3 = version == EPUB3 - let writeHtml o = fmap UTF8.fromStringLazy . - writeHtmlStringForEPUB version o + let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . + writeHtmlStringForEPUB version o epochtime <- floor <$> lift P.getPOSIXTime metadata <- getEPUBMetadata opts meta let mkEntry path content = toEntry path epochtime content diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index fb232e278..213756330 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -1,8 +1,8 @@ {-# LANGUAGE PatternGuards #-} {- -Copyright (c) 2011-2012, Sergey Astanin -All rights reserved. +Copyright (c) 2011-2012 Sergey Astanin + 2012-2017 John MacFarlane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,17 @@ 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. +{- | +Module : Text.Pandoc.Writers.FB2 +Copyright : Copyright (C) 2011-2012 Sergey Astanin + 2012-2017 John MacFarlane +License : GNU GPL, version 2 or above + +Maintainer : John MacFarlane +Stability : alpha +Portability : portable + +Conversion of 'Pandoc' documents to FB2 (FictionBook2) format. FictionBook is an XML-based e-book format. For more information see: <http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1> @@ -34,9 +44,9 @@ import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B8 import Data.Char (isAscii, isControl, isSpace, toLower) import Data.Either (lefts, rights) +import Data.Text (Text, pack) import Data.List (intercalate, intersperse, isPrefixOf, stripPrefix) import Network.HTTP (urlEncode) -import Network.URI (isURI) import Text.XML.Light import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC @@ -47,7 +57,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) -import Text.Pandoc.Shared (capitalize, isHeaderBlock, linesToPara, +import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, linesToPara, orderedListMarkers) -- | Data to be written at the end of the document: @@ -77,13 +87,13 @@ instance Show ImageMode where writeFB2 :: PandocMonad m => WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert - -> m String -- ^ FictionBook2 document (not encoded yet) + -> m Text -- ^ FictionBook2 document (not encoded yet) writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc pandocToFB2 :: PandocMonad m => WriterOptions -> Pandoc - -> FBM m String + -> FBM m Text pandocToFB2 opts (Pandoc meta blocks) = do modify (\s -> s { writerOptions = opts }) desc <- description meta @@ -94,7 +104,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do (imgs,missing) <- liftM imagesToFetch get >>= \s -> lift (fetchImages s) let body' = replaceImagesWithAlt missing body let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) - return $ xml_head ++ (showContent fb2_xml) ++ "\n" + return $ pack $ xml_head ++ (showContent fb2_xml) ++ "\n" where xml_head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" fb2_attrs = diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9f41f77d1..5ee8ab4ce 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.HTML - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -45,6 +45,8 @@ module Text.Pandoc.Writers.HTML ( ) where import Control.Monad.State import Data.Char (ord, toLower) +import Data.Text (Text) +import qualified Data.Text.Lazy as TL import Data.List (intersperse, isPrefixOf) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) @@ -67,7 +69,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.XML (escapeStringForXML, fromEntities) #if MIN_VERSION_blaze_markup(0,6,3) #else -import Text.Blaze.Internal (preEscapedString) +import Text.Blaze.Internal (preEscapedString, preEscapedText) #endif #if MIN_VERSION_blaze_html(0,5,1) import qualified Text.Blaze.XHtml5 as H5 @@ -77,7 +79,7 @@ import qualified Text.Blaze.Html5 as H5 import Control.Monad.Except (throwError) import Data.Aeson (Value) import System.FilePath (takeExtension, takeBaseName) -import Text.Blaze.Html.Renderer.String (renderHtml) +import Text.Blaze.Html.Renderer.Text (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A import Text.Pandoc.Class (PandocMonad, report) @@ -123,7 +125,7 @@ nl opts = if writerWrapText opts == WrapNone else preEscapedString "\n" -- | Convert Pandoc document to Html 5 string. -writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml5String = writeHtmlString' defaultWriterState{ stHtml5 = True } @@ -132,7 +134,7 @@ writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 4 string. -writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml4String = writeHtmlString' defaultWriterState{ stHtml5 = False } @@ -142,38 +144,39 @@ writeHtml4 = writeHtml' defaultWriterState{ stHtml5 = False } -- | Convert Pandoc document to Html appropriate for an epub version. writeHtmlStringForEPUB :: PandocMonad m - => EPUBVersion -> WriterOptions -> Pandoc -> m String -writeHtmlStringForEPUB version = writeHtmlString' + => EPUBVersion -> WriterOptions -> Pandoc + -> m Text +writeHtmlStringForEPUB version o = writeHtmlString' defaultWriterState{ stHtml5 = version == EPUB3, - stEPUBVersion = Just version } + stEPUBVersion = Just version } o -- | Convert Pandoc document to Reveal JS HTML slide show. writeRevealJs :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeRevealJs = writeHtmlSlideShow' RevealJsSlides -- | Convert Pandoc document to S5 HTML slide show. writeS5 :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeS5 = writeHtmlSlideShow' S5Slides -- | Convert Pandoc document to Slidy HTML slide show. writeSlidy :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeSlidy = writeHtmlSlideShow' SlidySlides -- | Convert Pandoc document to Slideous HTML slide show. writeSlideous :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeSlideous = writeHtmlSlideShow' SlideousSlides -- | Convert Pandoc document to DZSlides HTML slide show. writeDZSlides :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeDZSlides = writeHtmlSlideShow' DZSlides writeHtmlSlideShow' :: PandocMonad m - => HTMLSlideVariant -> WriterOptions -> Pandoc -> m String + => HTMLSlideVariant -> WriterOptions -> Pandoc -> m Text writeHtmlSlideShow' variant = writeHtmlString' defaultWriterState{ stSlideVariant = variant , stHtml5 = case variant of @@ -185,12 +188,15 @@ writeHtmlSlideShow' variant = writeHtmlString' NoSlides -> False } +renderHtml' :: Html -> Text +renderHtml' = TL.toStrict . renderHtml + writeHtmlString' :: PandocMonad m - => WriterState -> WriterOptions -> Pandoc -> m String + => WriterState -> WriterOptions -> Pandoc -> m Text writeHtmlString' st opts d = do (body, context) <- evalStateT (pandocToHtml opts d) st case writerTemplate opts of - Nothing -> return $ renderHtml body + Nothing -> return $ renderHtml' body Just tpl -> do -- warn if empty lang when (isNothing (getField "lang" context :: Maybe String)) $ @@ -205,12 +211,12 @@ writeHtmlString' st opts d = do report $ NoTitleElement fallback return $ resetField "pagetitle" fallback context return $ renderTemplate' tpl $ - defField "body" (renderHtml body) context' + defField "body" (renderHtml' body) context' writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html writeHtml' st opts d = do case writerTemplate opts of - Just _ -> preEscapedString <$> writeHtmlString' st opts d + Just _ -> preEscapedText <$> writeHtmlString' st opts d Nothing -> do (body, _) <- evalStateT (pandocToHtml opts d) st return body @@ -222,8 +228,8 @@ pandocToHtml :: PandocMonad m -> StateT WriterState m (Html, Value) pandocToHtml opts (Pandoc meta blocks) = do metadata <- metaToJSON opts - (fmap renderHtml . blockListToHtml opts) - (fmap renderHtml . inlineListToHtml opts) + (fmap renderHtml' . blockListToHtml opts) + (fmap renderHtml' . inlineListToHtml opts) meta let stringifyHTML = escapeStringForXML . stringify let authsMeta = map stringifyHTML $ docAuthors meta @@ -277,10 +283,10 @@ pandocToHtml opts (Pandoc meta blocks) = do Nothing -> id else id) $ (if stMath st - then defField "math" (renderHtml math) + then defField "math" (renderHtml' math) else id) $ defField "quotes" (stQuotes st) $ - maybe id (defField "toc" . renderHtml) toc $ + maybe id (defField "toc" . renderHtml') toc $ defField "author-meta" authsMeta $ maybe id (defField "date-meta") (normalizeDate dateMeta) $ defField "pagetitle" (stringifyHTML (docTitle meta)) $ @@ -463,7 +469,7 @@ parseMailto s = do obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt -obfuscateLink opts attr (renderHtml -> txt) s = +obfuscateLink opts attr (TL.unpack . renderHtml -> txt) s = let meth = writerEmailObfuscation opts s' = map toLower (take 7 s) ++ drop 7 s in case parseMailto s' of @@ -521,7 +527,7 @@ attrsToHtml opts (id',classes',keyvals) = imgAttrsToHtml :: WriterOptions -> Attr -> [Attribute] imgAttrsToHtml opts attr = attrsToHtml opts (ident,cls,kvs') ++ - toAttrs (dimensionsToAttrList opts attr) + toAttrs (dimensionsToAttrList attr) where (ident,cls,kvs) = attr kvs' = filter isNotDim kvs @@ -529,14 +535,13 @@ imgAttrsToHtml opts attr = isNotDim ("height", _) = False isNotDim _ = True -dimensionsToAttrList :: WriterOptions -> Attr -> [(String, String)] -dimensionsToAttrList opts attr = (go Width) ++ (go Height) +dimensionsToAttrList :: Attr -> [(String, String)] +dimensionsToAttrList attr = (go Width) ++ (go Height) where go dir = case (dimension dir attr) of - (Just (Percent a)) -> [("style", show dir ++ ":" ++ show (Percent a))] - (Just dim) -> [(show dir, showInPixel opts dim)] - _ -> [] - + (Just (Pixel a)) -> [(show dir, show a)] + (Just x) -> [("style", show dir ++ ":" ++ show x)] + Nothing -> [] imageExts :: [String] imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", @@ -974,7 +979,7 @@ inlineToHtml opts inline = do (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt lift $ obfuscateLink opts attr linkText s - (Link attr txt (s,tit)) -> do + (Link (ident,classes,kvs) txt (s,tit)) -> do linkText <- inlineListToHtml opts txt slideVariant <- gets stSlideVariant let s' = case s of @@ -984,13 +989,13 @@ inlineToHtml opts inline = do in '#' : prefix ++ xs _ -> s let link = H.a ! A.href (toValue s') $ linkText - let link' = if txt == [Str (unEscapeString s)] - then link ! A.class_ "uri" - else link - let link'' = addAttrs opts attr link' + let attr = if txt == [Str (unEscapeString s)] + then (ident, "uri" : classes, kvs) + else (ident, classes, kvs) + let link' = addAttrs opts attr link return $ if null tit - then link'' - else link'' ! A.title (toValue tit) + then link' + else link' ! A.title (toValue tit) (Image attr txt (s,tit)) | treatAsImage s -> do let alternate' = stringify txt slideVariant <- gets stSlideVariant diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index eae1377cd..1ad9acd40 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2014 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Haddock - Copyright : Copyright (C) 2014 John MacFarlane + Copyright : Copyright (C) 2014-2015,2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,8 +35,8 @@ Haddock: <http://www.haskell.org/haddock/doc/html/> module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad.State import Data.Default +import Data.Text (Text) import Data.List (intersperse, transpose) -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging @@ -53,14 +53,14 @@ instance Default WriterState where def = WriterState{ stNotes = [] } -- | Convert Pandoc to Haddock. -writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHaddock opts document = evalStateT (pandocToHaddock opts{ writerWrapText = writerWrapText opts } document) def -- | Return haddock representation of document. pandocToHaddock :: PandocMonad m - => WriterOptions -> Pandoc -> StateT WriterState m String + => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToHaddock opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -68,13 +68,13 @@ pandocToHaddock opts (Pandoc meta blocks) = do body <- blockListToHaddock opts blocks st <- get notes' <- notesToHaddock opts (reverse $ stNotes st) - let render' :: Doc -> String + let render' :: Doc -> Text render' = render colwidth let main = render' $ body <> (if isEmpty notes' then empty else blankline <> notes') metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToHaddock opts) - (fmap (render colwidth) . inlineListToHaddock opts) + (fmap render' . blockListToHaddock opts) + (fmap render' . inlineListToHaddock opts) meta let context = defField "body" main $ metadata diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index cd3cac5a7..2884bc532 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -4,7 +4,7 @@ {- | Module : Text.Pandoc.Writers.ICML - Copyright : Copyright (C) 2013-2016 github.com/mb21 + Copyright : Copyright (C) 2013-2017 github.com/mb21 License : GNU GPL, version 2 or above Stability : alpha @@ -21,16 +21,15 @@ import Control.Monad.State import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix) import qualified Data.Set as Set import Data.Text as Text (breakOnAll, pack) -import Network.URI (isURI) +import Data.Text (Text) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition -import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara, splitBy) +import Text.Pandoc.Shared (isURI, linesToPara, splitBy) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared @@ -129,11 +128,12 @@ footnoteName = "Footnote" citeName = "Cite" -- | Convert Pandoc document to string in ICML format. -writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeICML opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + render' :: Doc -> Text render' = render colwidth renderMeta f s = liftM (render' . fst) $ runStateT (f opts [] s) defaultWriterState metadata <- metaToJSON opts @@ -550,10 +550,7 @@ imageICML opts style attr (src, _) = do report $ CouldNotDetermineImageSize src msg return def) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return def) let (ow, oh) = sizeInPoints imgS (imgWidth, imgHeight) = desiredSizeInPoints opts attr imgS diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index aca7dc969..1a8d80747 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -33,6 +33,7 @@ https://jats.nlm.nih.gov/publishing/tag-library/1.1d3/element/mml-math.html module Text.Pandoc.Writers.JATS ( writeJATS ) where import Control.Monad.Reader import Data.Char (toLower) +import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isSuffixOf, partition) import Data.Maybe (fromMaybe) @@ -81,12 +82,12 @@ authorToJATS opts name' = do in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) -writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJATS opts d = runReaderT (docToJATS opts d) JATS1_1 -- | Convert Pandoc document to string in JATS format. -docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m String +docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text docToJATS opts (Pandoc meta blocks) = do let isBackBlock (Div ("refs",_,_) _) = True isBackBlock _ = False @@ -96,7 +97,8 @@ docToJATS opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && TopLevelDefault == writerTopLevelDivision opts) @@ -111,10 +113,10 @@ docToJATS opts (Pandoc meta blocks) = do auths' <- mapM (authorToJATS opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . + (fmap (render' . vcat) . (mapM (elementToJATS opts' startLvl) . hierarchicalize)) - (fmap (render colwidth) . inlinesToJATS opts') + (fmap render' . inlinesToJATS opts') meta' main <- (render' . vcat) <$> (mapM (elementToJATS opts' startLvl) elements) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 000f4f8fb..80606d510 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -2,7 +2,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.LaTeX - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -42,8 +42,9 @@ import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, import Data.List (foldl', intercalate, intersperse, isInfixOf, nub, nubBy, stripPrefix, (\\)) import Data.Maybe (catMaybes, fromMaybe, isJust) +import Data.Text (Text) import qualified Data.Text as T -import Network.URI (isURI, unEscapeString) +import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, @@ -114,13 +115,13 @@ startingState options = WriterState { , stEmptyLine = True } -- | Convert Pandoc to LaTeX. -writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeLaTeX options document = evalStateT (pandocToLaTeX options document) $ startingState options -- | Convert Pandoc to LaTeX Beamer. -writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeBeamer options document = evalStateT (pandocToLaTeX options document) $ (startingState options){ stBeamer = True } @@ -128,7 +129,7 @@ writeBeamer options document = type LW m = StateT WriterState m pandocToLaTeX :: PandocMonad m - => WriterOptions -> Pandoc -> LW m String + => WriterOptions -> Pandoc -> LW m Text pandocToLaTeX options (Pandoc meta blocks) = do -- Strip off final 'references' header if --natbib or --biblatex let method = writerCiteMethod options @@ -146,9 +147,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToLaTeX) - (fmap (render colwidth) . inlineListToLaTeX) + (fmap render' . blockListToLaTeX) + (fmap render' . inlineListToLaTeX) meta let bookClasses = ["memoir","book","report","scrreprt","scrbook"] let documentClass = case P.parse pDocumentClass "template" template of @@ -180,8 +183,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do then toSlides blocks'' else return blocks'' body <- mapM (elementToLaTeX options) $ hierarchicalize blocks''' - (biblioTitle :: String) <- liftM (render colwidth) $ inlineListToLaTeX lastHeader - let main = render colwidth $ vsep body + (biblioTitle :: Text) <- render' <$> inlineListToLaTeX lastHeader + let main = render' $ vsep body st <- get titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta @@ -1062,6 +1065,9 @@ inlineToLaTeX (Link _ txt (src, _)) = src' <- stringToLaTeX URLString (escapeURI src) return $ text ("\\href{" ++ src' ++ "}{") <> contents <> char '}' +inlineToLaTeX il@(Image _ _ ('d':'a':'t':'a':':':_, _)) = do + report $ InlineNotRendered il + return empty inlineToLaTeX (Image attr _ (source, _)) = do setEmptyLine False modify $ \s -> s{ stGraphics = True } diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 1f3e17c16..0fc6afbdc 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -1,5 +1,6 @@ +{-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 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 @@ -18,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Man - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,6 +35,8 @@ import Control.Monad.State import Data.List (intercalate, intersperse, stripPrefix, sort) import qualified Data.Map as Map import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -62,36 +65,37 @@ defaultWriterState = WriterState { stNotes = [] , stHasTables = False } -- | Convert Pandoc to Man. -writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMan opts document = evalStateT (pandocToMan opts document) defaultWriterState -- | Return groff man representation of document. -pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m String +pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToMan opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth titleText <- inlineListToMan opts $ docTitle meta let title' = render' titleText let setFieldsFromTitle = - case break (== ' ') title' of - (cmdName, rest) -> case break (=='(') cmdName of - (xs, '(':ys) | not (null ys) && - last ys == ')' -> + case T.break (== ' ') title' of + (cmdName, rest) -> case T.break (=='(') cmdName of + (xs, ys) | "(" `T.isPrefixOf` ys + && ")" `T.isSuffixOf` ys -> defField "title" xs . - defField "section" (init ys) . - case splitBy (=='|') rest of + defField "section" (T.init $ T.drop 1 ys) . + case T.splitOn "|" rest of (ft:hds) -> - defField "footer" (trim ft) . + defField "footer" (T.strip ft) . defField "header" - (trim $ concat hds) + (T.strip $ mconcat hds) [] -> id _ -> defField "title" title' metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToMan opts) - (fmap (render colwidth) . inlineListToMan opts) + (fmap render' . blockListToMan opts) + (fmap render' . inlineListToMan opts) $ deleteMeta "title" meta body <- blockListToMan opts blocks notes <- gets stNotes diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 8e3ac3665..3ac677943 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Markdown - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,26 +34,25 @@ Conversion of 'Pandoc' documents to markdown-formatted plain text. Markdown: <http://daringfireball.net/projects/markdown/> -} module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where -import Control.Monad.Except (throwError) import Control.Monad.Reader import Control.Monad.State import Data.Char (chr, isPunctuation, isSpace, ord) import Data.Default import qualified Data.HashMap.Strict as H +import qualified Data.Map as M import Data.List (find, group, intersperse, sortBy, stripPrefix, transpose) import Data.Maybe (fromMaybe) import Data.Monoid (Any (..)) import Data.Ord (comparing) import qualified Data.Set as Set +import Data.Text (Text) import qualified Data.Text as T import qualified Data.Vector as V import Data.Yaml (Value (Array, Bool, Number, Object, String)) import Network.HTTP (urlEncode) -import Network.URI (isURI) import Text.HTML.TagSoup (Tag (..), isTagText, parseTags) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space) @@ -91,6 +90,9 @@ instance Default WriterEnv data WriterState = WriterState { stNotes :: Notes , stRefs :: Refs + , stKeys :: M.Map Key + (M.Map (Target, Attr) Int) + , stLastIdx :: Int , stIds :: Set.Set String , stNoteNum :: Int } @@ -98,12 +100,14 @@ data WriterState = WriterState { stNotes :: Notes instance Default WriterState where def = WriterState{ stNotes = [] , stRefs = [] + , stKeys = M.empty + , stLastIdx = 0 , stIds = Set.empty , stNoteNum = 1 } -- | Convert Pandoc to Markdown. -writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMarkdown opts document = evalMD (pandocToMarkdown opts{ writerWrapText = if isEnabled Ext_hard_line_breaks opts @@ -113,7 +117,7 @@ writeMarkdown opts document = -- | Convert Pandoc to plain text (like markdown, but without links, -- pictures, or inline formatting). -writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m String +writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m Text writePlain opts document = evalMD (pandocToMarkdown opts document) def{ envPlain = True } def @@ -177,15 +181,17 @@ jsonToYaml (Number n) = text $ show n jsonToYaml _ = empty -- | Return markdown representation of document. -pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m String +pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m Text pandocToMarkdown opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing isPlain <- asks envPlain + let render' :: Doc -> Text + render' = render colwidth . chomp metadata <- metaToJSON' - (fmap (render colwidth) . blockListToMarkdown opts) - (fmap (render colwidth) . blockToMarkdown opts . Plain) + (fmap render' . blockListToMarkdown opts) + (fmap render' . blockToMarkdown opts . Plain) meta let title' = maybe empty text $ getField "title" metadata let authors' = maybe [] (map text) $ getField "author" metadata @@ -213,8 +219,6 @@ pandocToMarkdown opts (Pandoc meta blocks) = do else blocks body <- blockListToMarkdown opts blocks' notesAndRefs' <- notesAndRefs opts - let render' :: Doc -> String - render' = render colwidth . chomp let main = render' $ body <> notesAndRefs' let context = defField "toc" (render' toc) $ defField "body" main @@ -241,7 +245,7 @@ keyToMarkdown opts (label', (src, tit), attr) = do else space <> "\"" <> text tit <> "\"" return $ nest 2 $ hang 2 ("[" <> label' <> "]:" <> space) (text src <> tit') - <> linkAttributes opts attr + <+> linkAttributes opts attr -- | Return markdown representation of notes. notesToMarkdown :: PandocMonad m => WriterOptions -> [[Block]] -> MD m Doc @@ -471,6 +475,8 @@ blockToMarkdown' opts (Header level attr inlines) = do space <> attrsToMarkdown attr | otherwise -> empty contents <- inlineListToMarkdown opts $ + -- ensure no newlines; see #3736 + walk lineBreakToSpace $ if level == 1 && plain then capitalize inlines else inlines @@ -568,7 +574,7 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do gridTable opts blockListToMarkdown (all null headers) aligns' widths' headers rows | isEnabled Ext_raw_html opts -> fmap (id,) $ - text <$> + (text . T.unpack) <$> (writeHtml5String def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ caption'' $$ blankline @@ -788,7 +794,7 @@ blockListToMarkdown opts blocks = do isListBlock _ = False commentSep = if isEnabled Ext_raw_html opts then RawBlock "html" "<!-- -->\n" - else RawBlock "markdown" " " + else RawBlock "markdown" " \n" mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat getKey :: Doc -> Key @@ -798,20 +804,49 @@ getKey = toKey . render Nothing -- Prefer label if possible; otherwise, generate a unique key. getReference :: PandocMonad m => Attr -> Doc -> Target -> MD m Doc getReference attr label target = do - st <- get - let keys = map (\(l,_,_) -> getKey l) (stRefs st) - case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of + refs <- gets stRefs + case find (\(_,t,a) -> t == target && a == attr) refs of Just (ref, _, _) -> return ref Nothing -> do - label' <- case getKey label `elem` keys of - True -> -- label is used; generate numerical label - case find (\n -> Key n `notElem` keys) $ - map show [1..(10000 :: Integer)] of - Just x -> return $ text x - Nothing -> throwError $ PandocSomeError "no unique label" - False -> return label - modify (\s -> s{ stRefs = (label', target, attr) : stRefs st }) - return label' + keys <- gets stKeys + case M.lookup (getKey label) keys of + Nothing -> do -- no other refs with this label + (lab', idx) <- if isEmpty label + then do + i <- (+ 1) <$> gets stLastIdx + modify $ \s -> s{ stLastIdx = i } + return (text (show i), i) + else return (label, 0) + modify (\s -> s{ + stRefs = (lab', target, attr) : refs, + stKeys = M.insert (getKey label) + (M.insert (target, attr) idx mempty) + (stKeys s) }) + return lab' + + Just km -> do -- we have refs with this label + case M.lookup (target, attr) km of + Just i -> do + let lab' = label <> if i == 0 + then mempty + else text (show i) + -- make sure it's in stRefs; it may be + -- a duplicate that was printed in a previous + -- block: + when ((lab', target, attr) `notElem` refs) $ + modify (\s -> s{ + stRefs = (lab', target, attr) : refs }) + return lab' + Nothing -> do -- but this one is to a new target + i <- (+ 1) <$> gets stLastIdx + modify $ \s -> s{ stLastIdx = i } + let lab' = text (show i) + modify (\s -> s{ + stRefs = (lab', target, attr) : refs, + stKeys = M.insert (getKey label) + (M.insert (target, attr) i km) + (stKeys s) }) + return lab' -- | Convert list of Pandoc inline elements to markdown. inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m Doc @@ -821,7 +856,8 @@ inlineListToMarkdown opts lst = do where go [] = return empty go (i:is) = case i of (Link _ _ _) -> case is of - -- If a link is followed by another link or '[' we don't shortcut + -- If a link is followed by another link, or '[', '(' or ':' + -- then we don't shortcut (Link _ _ _):_ -> unshortcutable Space:(Link _ _ _):_ -> unshortcutable Space:(Str('[':_)):_ -> unshortcutable @@ -831,9 +867,17 @@ inlineListToMarkdown opts lst = do SoftBreak:(Str('[':_)):_ -> unshortcutable SoftBreak:(RawInline _ ('[':_)):_ -> unshortcutable SoftBreak:(Cite _ _):_ -> unshortcutable + LineBreak:(Link _ _ _):_ -> unshortcutable + LineBreak:(Str('[':_)):_ -> unshortcutable + LineBreak:(RawInline _ ('[':_)):_ -> unshortcutable + LineBreak:(Cite _ _):_ -> unshortcutable (Cite _ _):_ -> unshortcutable Str ('[':_):_ -> unshortcutable + Str ('(':_):_ -> unshortcutable + Str (':':_):_ -> unshortcutable (RawInline _ ('[':_)):_ -> unshortcutable + (RawInline _ ('(':_)):_ -> unshortcutable + (RawInline _ (':':_)):_ -> unshortcutable (RawInline _ (' ':'[':_)):_ -> unshortcutable _ -> shortcutable _ -> shortcutable @@ -890,12 +934,14 @@ inlineToMarkdown opts (Span attrs ils) = do isEnabled Ext_native_spans opts -> tagWithAttrs "span" attrs <> contents <> text "</span>" | otherwise -> contents +inlineToMarkdown _ (Emph []) = return empty inlineToMarkdown opts (Emph lst) = do plain <- asks envPlain contents <- inlineListToMarkdown opts lst return $ if plain then "_" <> contents <> "_" else "*" <> contents <> "*" +inlineToMarkdown _ (Strong []) = return empty inlineToMarkdown opts (Strong lst) = do plain <- asks envPlain if plain @@ -903,6 +949,7 @@ inlineToMarkdown opts (Strong lst) = do else do contents <- inlineListToMarkdown opts lst return $ "**" <> contents <> "**" +inlineToMarkdown _ (Strikeout []) = return empty inlineToMarkdown opts (Strikeout lst) = do contents <- inlineListToMarkdown opts lst return $ if isEnabled Ext_strikeout opts @@ -910,6 +957,7 @@ inlineToMarkdown opts (Strikeout lst) = do else if isEnabled Ext_raw_html opts then "<s>" <> contents <> "</s>" else contents +inlineToMarkdown _ (Superscript []) = return empty inlineToMarkdown opts (Superscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst @@ -922,6 +970,7 @@ inlineToMarkdown opts (Superscript lst) = in case mapM toSuperscript rendered of Just r -> text r Nothing -> text $ "^(" ++ rendered ++ ")" +inlineToMarkdown _ (Subscript []) = return empty inlineToMarkdown opts (Subscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst @@ -1064,7 +1113,8 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit)) | isEnabled Ext_raw_html opts && not (isEnabled Ext_link_attributes opts) && attr /= nullAttr = -- use raw HTML - (text . trim) <$> writeHtml5String def (Pandoc nullMeta [Plain [lnk]]) + (text . T.unpack . T.strip) <$> + writeHtml5String def (Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1103,7 +1153,8 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit)) | isEnabled Ext_raw_html opts && not (isEnabled Ext_link_attributes opts) && attr /= nullAttr = -- use raw HTML - (text . trim) <$> writeHtml5String def (Pandoc nullMeta [Plain [img]]) + (text . T.unpack . T.strip) <$> + writeHtml5String def (Pandoc nullMeta [Plain [img]]) | otherwise = do plain <- asks envPlain let txt = if null alternate || alternate == [Str source] @@ -1154,3 +1205,8 @@ toSubscript c Just $ chr (0x2080 + (ord c - 48)) | isSpace c = Just c | otherwise = Nothing + +lineBreakToSpace :: Inline -> Inline +lineBreakToSpace LineBreak = Space +lineBreakToSpace SoftBreak = Space +lineBreakToSpace x = x diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index def245e38..c70e5b786 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.MediaWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,7 +34,7 @@ import Control.Monad.Reader import Control.Monad.State import Data.List (intercalate) import qualified Data.Set as Set -import Network.URI (isURI) +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -60,14 +60,14 @@ data WriterReader = WriterReader { type MediaWikiWriter m = ReaderT WriterReader (StateT WriterState m) -- | Convert Pandoc to MediaWiki. -writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMediaWiki opts document = let initialState = WriterState { stNotes = False, stOptions = opts } env = WriterReader { options = opts, listLevel = [], useTags = False } in evalStateT (runReaderT (pandocToMediaWiki document) env) initialState -- | Return MediaWiki representation of document. -pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m String +pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m Text pandocToMediaWiki (Pandoc meta blocks) = do opts <- asks options metadata <- metaToJSON opts @@ -82,7 +82,8 @@ pandocToMediaWiki (Pandoc meta blocks) = do let main = body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata - return $ case writerTemplate opts of + return $ pack + $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate' tpl context diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 534f26a5a..c5c3d9f5b 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Ms - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -44,6 +44,7 @@ import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Printf ( printf ) import qualified Data.Text as T +import Data.Text (Text) import qualified Data.Map as Map import Data.Maybe ( catMaybes, fromMaybe ) import Data.List ( intersperse, intercalate, sort ) @@ -85,17 +86,18 @@ type Note = [Block] type MS = StateT WriterState -- | Convert Pandoc to Ms. -writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMs opts document = evalStateT (pandocToMs opts document) defaultWriterState -- | Return groff ms representation of document. -pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m String +pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m Text pandocToMs opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts (fmap render' . blockListToMs opts) (fmap render' . inlineListToMs' opts) @@ -108,9 +110,9 @@ pandocToMs opts (Pandoc meta blocks) = do hasHighlighting <- gets stHighlighting let highlightingMacros = if hasHighlighting then case writerHighlightStyle opts of - Nothing -> "" + Nothing -> mempty Just sty -> render' $ styleToMs sty - else "" + else mempty let context = defField "body" main $ defField "has-inline-math" hasInlineMath diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 8f6493975..85e0b5467 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -43,6 +43,7 @@ even though it is supported only in Emacs Muse. -} module Text.Pandoc.Writers.Muse (writeMuse) where import Control.Monad.State +import Data.Text (Text) import Data.List (intersperse, transpose, isInfixOf) import System.FilePath (takeExtension) import Text.Pandoc.Class (PandocMonad) @@ -53,6 +54,7 @@ import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared +import qualified Data.Set as Set type Notes = [[Block]] data WriterState = @@ -60,33 +62,37 @@ data WriterState = , stOptions :: WriterOptions , stTopLevel :: Bool , stInsideBlock :: Bool + , stIds :: Set.Set String } -- | Convert Pandoc to Muse. writeMuse :: PandocMonad m => WriterOptions -> Pandoc - -> m String + -> m Text writeMuse opts document = let st = WriterState { stNotes = [] , stOptions = opts , stTopLevel = True , stInsideBlock = False + , stIds = Set.empty } in evalStateT (pandocToMuse document) st -- | Return Muse representation of document. pandocToMuse :: PandocMonad m => Pandoc - -> StateT WriterState m String + -> StateT WriterState m Text pandocToMuse (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render Nothing metadata <- metaToJSON opts - (fmap (render Nothing) . blockListToMuse) - (fmap (render Nothing) . inlineListToMuse) + (fmap render' . blockListToMuse) + (fmap render' . inlineListToMuse) meta body <- blockListToMuse blocks notes <- liftM (reverse . stNotes) get >>= notesToMuse @@ -184,8 +190,14 @@ blockToMuse (DefinitionList items) = do let ind = offset label'' return $ hang ind label'' contents blockToMuse (Header level (ident,_,_) inlines) = do + opts <- gets stOptions contents <- inlineListToMuse inlines - let attr' = if null ident + + ids <- gets stIds + let autoId = uniqueIdent inlines ids + modify $ \st -> st{ stIds = Set.insert autoId ids } + + let attr' = if null ident || (isEnabled Ext_auto_identifiers opts && ident == autoId) then empty else "#" <> text ident <> cr let header' = text $ replicate level '*' @@ -207,7 +219,7 @@ blockToMuse (Table caption _ _ headers rows) = do let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks where h = maximum (1 : map height blocks) sep' = lblock (length sep) $ vcat (map text $ replicate h sep) - let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars + let makeRow sep = (" " <>) . (hpipeBlocks sep . zipWith lblock widthsInChars) let head' = makeRow " || " headers' let rowSeparator = if noHeaders then " | " else " | " rows'' <- mapM (\row -> do cols <- mapM blockListToMuse row @@ -215,7 +227,7 @@ blockToMuse (Table caption _ _ headers rows) = do let body = vcat rows'' return $ (if noHeaders then empty else head') $$ body - $$ (if null caption then empty else "|+ " <> caption' <> " +|") + $$ (if null caption then empty else " |+ " <> caption' <> " +|") $$ blankline blockToMuse (Div _ bs) = blockListToMuse bs blockToMuse Null = return empty diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index b031a0231..3ef33f05c 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 : Text.Pandoc.Writers.Native - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -30,6 +30,7 @@ Conversion of a 'Pandoc' document to a string representation. -} module Text.Pandoc.Writers.Native ( writeNative ) where +import Data.Text (Text) import Data.List (intersperse) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition @@ -67,7 +68,7 @@ prettyBlock (Div attr blocks) = prettyBlock block = text $ show block -- | Prettyprint Pandoc document. -writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeNative opts (Pandoc meta blocks) = return $ let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 395ef0a96..1da051380 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 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 : Text.Pandoc.Writers.ODT - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,18 +35,18 @@ import Control.Monad.State import qualified Data.ByteString.Lazy as B import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) +import qualified Data.Text.Lazy as TL import System.FilePath (takeDirectory, takeExtension, (<.>)) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition -import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.Pandoc.Pretty import Text.Pandoc.Shared (stringify) -import Text.Pandoc.UTF8 (fromStringLazy) +import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.Writers.Shared (fixDisplayMath) @@ -89,7 +89,7 @@ pandocToODT opts doc@(Pandoc meta _) = do newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc' epochtime <- floor `fmap` (lift P.getPOSIXTime) let contentEntry = toEntry "content.xml" epochtime - $ fromStringLazy newContents + $ fromTextLazy $ TL.fromStrict newContents picEntries <- gets stEntries let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries @@ -178,10 +178,7 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError modify $ \st -> st{ stEntries = entry : entries } return $ Image newattr lab (newsrc, t)) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return $ Emph lab) transformPicMath _ (Math t math) = do diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 98510c40f..4a0a317fa 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} {- -Copyright (C) 2013-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 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 : Text.Pandoc.Writers.OPML - Copyright : Copyright (C) 2013-2015 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -30,6 +30,8 @@ Conversion of 'Pandoc' documents to OPML XML. -} module Text.Pandoc.Writers.OPML ( writeOPML) where import Control.Monad.Except (throwError) +import Data.Text (Text, unpack) +import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Compat.Time @@ -45,7 +47,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -- | Convert Pandoc document to string in OPML format. -writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOPML opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto @@ -54,7 +56,7 @@ writeOPML opts (Pandoc meta blocks) = do meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta metadata <- metaToJSON opts (writeMarkdown def . Pandoc nullMeta) - (\ils -> trimr <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) + (\ils -> T.stripEnd <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) meta' main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements) let context = defField "body" main metadata @@ -63,9 +65,9 @@ writeOPML opts (Pandoc meta blocks) = do Just tpl -> renderTemplate' tpl context -writeHtmlInlines :: PandocMonad m => [Inline] -> m String +writeHtmlInlines :: PandocMonad m => [Inline] -> m Text writeHtmlInlines ils = - trim <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) + T.strip <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> String @@ -95,9 +97,10 @@ elementToOPML opts (Sec _ _num _ title elements) = do (blocks, rest) = span isBlk elements htmlIls <- writeHtmlInlines title md <- if null blocks - then return [] + then return mempty else do blks <- mapM fromBlk blocks writeMarkdown def $ Pandoc nullMeta blks - let attrs = [("text", htmlIls)] ++ [("_note", md) | not (null blocks)] + let attrs = [("text", unpack htmlIls)] ++ + [("_note", unpack md) | not (null blocks)] o <- mapM (elementToOPML opts) rest return $ inTags True "outline" attrs $ vcat o diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 491069343..58295684e 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2008-2015 Andrea Rossato <andrea.rossato@ing.unitn.it> +Copyright (C) 2008-2017 Andrea Rossato <andrea.rossato@ing.unitn.it> and John MacFarlane. This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.OpenDocument - Copyright : Copyright (C) 2008-2015 Andrea Rossato and John MacFarlane + Copyright : Copyright (C) 2008-2017 Andrea Rossato and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Andrea Rossato <andrea.rossato@ing.unitn.it> @@ -36,6 +36,7 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State hiding (when) import Data.Char (chr) import Data.List (sortBy) +import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) import qualified Data.Set as Set @@ -195,17 +196,18 @@ handleSpaces s rm [] = empty -- | Convert Pandoc document to string in OpenDocument format. -writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOpenDocument opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth ((body, metadata),s) <- flip runStateT defaultWriterState $ do m <- metaToJSON opts - (fmap (render colwidth) . blocksToOpenDocument opts) - (fmap (render colwidth) . inlinesToOpenDocument opts) + (fmap render' . blocksToOpenDocument opts) + (fmap render' . inlinesToOpenDocument opts) meta b <- render' `fmap` blocksToOpenDocument opts blocks return (b, m) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index fc6608450..e8f48da00 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2010-2015 Puneeth Chaganti <punchagan@gmail.com> - Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>, - and John MacFarlane <jgm@berkeley.edu> + 2010-2017 John MacFarlane <jgm@berkeley.edu> + 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -21,10 +21,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Org - Copyright : Copyright (C) 2010-2015 Puneeth Chaganti and John MacFarlane + Copyright : © 2010-2015 Puneeth Chaganti <punchagan@gmail.com> + 2010-2017 John MacFarlane <jgm@berkeley.edu> + 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> License : GNU GPL, version 2 or above - Maintainer : Puneeth Chaganti <punchagan@gmail.com> + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Stability : alpha Portability : portable @@ -32,9 +34,10 @@ Conversion of 'Pandoc' documents to Emacs Org-Mode. Org-Mode: <http://orgmode.org> -} -module Text.Pandoc.Writers.Org ( writeOrg) where +module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad.State import Data.Char (isAlphaNum, toLower) +import Data.Text (Text) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -54,7 +57,7 @@ data WriterState = type Org = StateT WriterState -- | Convert Pandoc to Org. -writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOrg opts document = do let st = WriterState { stNotes = [], stHasMath = False, @@ -62,22 +65,24 @@ writeOrg opts document = do evalStateT (pandocToOrg document) st -- | Return Org representation of document. -pandocToOrg :: PandocMonad m => Pandoc -> Org m String +pandocToOrg :: PandocMonad m => Pandoc -> Org m Text pandocToOrg (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToOrg) - (fmap (render colwidth) . inlineListToOrg) + (fmap render' . blockListToOrg) + (fmap render' . inlineListToOrg) meta body <- blockListToOrg blocks notes <- gets (reverse . stNotes) >>= notesToOrg hasMath <- gets stHasMath - let main = render colwidth $ foldl ($+$) empty $ [body, notes] + let main = render colwidth . foldl ($+$) empty $ [body, notes] let context = defField "body" main - $ defField "math" hasMath + . defField "math" hasMath $ metadata case writerTemplate opts of Nothing -> return main @@ -86,8 +91,7 @@ pandocToOrg (Pandoc meta blocks) = do -- | Return Org representation of notes. notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc notesToOrg notes = - mapM (\(num, note) -> noteToOrg num note) (zip [1..] notes) >>= - return . vsep + vsep <$> zipWithM noteToOrg [1..] notes -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m Doc @@ -219,16 +223,16 @@ blockToOrg (Table caption' _ _ headers rows) = do -- FIXME: Org doesn't allow blocks with height more than 1. let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (1 : map height blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") + sep' = lblock 3 $ vcat (replicate h (text " | ")) + beg = lblock 2 $ vcat (replicate h (text "| ")) + end = lblock 2 $ vcat (replicate h (text " |")) middle = hcat $ intersperse sep' blocks let makeRow = hpipeBlocks . zipWith lblock widthsInChars let head' = makeRow headers' rows' <- mapM (\row -> do cols <- mapM blockListToOrg row return $ makeRow cols) rows let border ch = char '|' <> char ch <> - (hcat $ intersperse (char ch <> char '+' <> char ch) $ + (hcat . intersperse (char ch <> char '+' <> char ch) $ map (\l -> text $ replicate l ch) widthsInChars) <> char ch <> char '|' let body = vcat rows' @@ -249,8 +253,7 @@ blockToOrg (OrderedList (start, _, delim) items) = do let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m in m ++ replicate s ' ') markers - contents <- mapM (\(item, num) -> orderedListItemToOrg item num) $ - zip markers' items + contents <- zipWithM orderedListItemToOrg markers' items -- ensure that sublists have preceding blank line return $ blankline $$ vcat contents $$ blankline blockToOrg (DefinitionList items) = do @@ -277,8 +280,8 @@ definitionListItemToOrg :: PandocMonad m => ([Inline], [[Block]]) -> Org m Doc definitionListItemToOrg (label, defs) = do label' <- inlineListToOrg label - contents <- liftM vcat $ mapM blockListToOrg defs - return $ hang 2 "- " $ label' <> " :: " <> (contents <> cr) + contents <- vcat <$> mapM blockListToOrg defs + return . hang 2 "- " $ label' <> " :: " <> (contents <> cr) -- | Convert list of key/value pairs to Org :PROPERTIES: drawer. propertiesDrawer :: Attr -> Doc @@ -310,13 +313,13 @@ attrHtml (ident, classes, kvs) = blockListToOrg :: PandocMonad m => [Block] -- ^ List of block elements -> Org m Doc -blockListToOrg blocks = mapM blockToOrg blocks >>= return . vcat +blockListToOrg blocks = vcat <$> mapM blockToOrg blocks -- | Convert list of Pandoc inline elements to Org. inlineListToOrg :: PandocMonad m => [Inline] -> Org m Doc -inlineListToOrg lst = mapM inlineToOrg lst >>= return . hcat +inlineListToOrg lst = hcat <$> mapM inlineToOrg lst -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m Doc @@ -348,7 +351,7 @@ inlineToOrg (Quoted DoubleQuote lst) = do return $ "\"" <> contents <> "\"" inlineToOrg (Cite _ lst) = inlineListToOrg lst inlineToOrg (Code _ str) = return $ "=" <> text str <> "=" -inlineToOrg (Str str) = return $ text $ escapeString str +inlineToOrg (Str str) = return . text $ escapeString str inlineToOrg (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 24898d62e..59f6553e2 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 : Text.Pandoc.Writers.RST - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,7 +35,7 @@ import Control.Monad.State import Data.Char (isSpace, toLower) import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (fromMaybe) -import Network.URI (isURI) +import Data.Text (Text, stripEnd) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging @@ -57,33 +57,36 @@ data WriterState = , stHasRawTeX :: Bool , stOptions :: WriterOptions , stTopLevel :: Bool + , stLastNested :: Bool } type RST = StateT WriterState -- | Convert Pandoc to RST. -writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, - stTopLevel = True} + stTopLevel = True, stLastNested = False} evalStateT (pandocToRST document) st -- | Return RST representation of document. -pandocToRST :: PandocMonad m => Pandoc -> RST m String +pandocToRST :: PandocMonad m => Pandoc -> RST m Text pandocToRST (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth let subtit = case lookupMeta "subtitle" meta of Just (MetaBlocks [Plain xs]) -> xs _ -> [] title <- titleToRST (docTitle meta) subtit metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToRST) - (fmap (trimr . render colwidth) . inlineListToRST) + (fmap render' . blockListToRST) + (fmap (stripEnd . render') . inlineListToRST) $ B.deleteMeta "title" $ B.deleteMeta "subtitle" meta body <- blockListToRST' True $ case writerTemplate opts of Just _ -> normalizeHeadings 1 blocks @@ -94,7 +97,7 @@ pandocToRST (Pandoc meta blocks) = do pics <- gets (reverse . stImages) >>= pictRefsToRST hasMath <- gets stHasMath rawTeX <- gets stHasRawTeX - let main = render colwidth $ foldl ($+$) empty $ [body, notes, refs, pics] + let main = render' $ foldl ($+$) empty $ [body, notes, refs, pics] let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ defField "toc-depth" (show $ writerTOCDepth opts) @@ -343,11 +346,32 @@ blockListToRST' :: PandocMonad m -> RST m Doc blockListToRST' topLevel blocks = do tl <- gets stTopLevel - modify (\s->s{stTopLevel=topLevel}) - res <- vcat `fmap` mapM blockToRST blocks + modify (\s->s{stTopLevel=topLevel, stLastNested=False}) + res <- vcat `fmap` mapM blockToRST' blocks modify (\s->s{stTopLevel=tl}) return res +blockToRST' :: PandocMonad m => Block -> RST m Doc +blockToRST' (x@BlockQuote{}) = do + lastNested <- gets stLastNested + res <- blockToRST x + modify (\s -> s{stLastNested = True}) + return $ if lastNested + then ".." $+$ res + else res +blockToRST' x = do + modify (\s -> s{stLastNested = + case x of + Para [Image _ _ (_,'f':'i':'g':':':_)] -> True + Para{} -> False + Plain{} -> False + Header{} -> False + LineBlock{} -> False + HorizontalRule -> False + _ -> True + }) + blockToRST x + blockListToRST :: PandocMonad m => [Block] -- ^ List of block elements -> RST m Doc diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 67f0fc2e0..5c990f324 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 : Text.Pandoc.Writers.RTF - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -34,6 +34,8 @@ import Control.Monad.Except (catchError, throwError) import qualified Data.ByteString as B import Data.Char (chr, isDigit, ord) import Data.List (intercalate, isSuffixOf) +import Data.Text (Text) +import qualified Data.Text as T import qualified Data.Map as M import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P @@ -92,15 +94,12 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = catchError report $ CouldNotDetermineMimeType src return x) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return x) rtfEmbedImage _ x = return x -- | Convert Pandoc to a string in rich text format. -writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRTF options doc = do -- handle images Pandoc meta@(Meta metamap) blocks <- walkM (rtfEmbedImage options) doc @@ -126,7 +125,8 @@ writeRTF options doc = do then defField "toc" toc else id) $ metadata - return $ case writerTemplate options of + return $ T.pack + $ case writerTemplate options of Just tpl -> renderTemplate' tpl context Nothing -> case reverse body of ('\n':_) -> body diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 615733a78..2047285eb 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2013-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 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 : Text.Pandoc.Writers.Shared - Copyright : Copyright (C) 2013-2015 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -62,10 +62,10 @@ import Text.Pandoc.XML (escapeStringForXML) -- Variables overwrite metadata fields with the same names. -- If multiple variables are set with the same name, a list is -- assigned. Does nothing if 'writerTemplate' is Nothing. -metaToJSON :: (Functor m, Monad m) +metaToJSON :: (Functor m, Monad m, ToJSON a) => WriterOptions - -> ([Block] -> m String) - -> ([Inline] -> m String) + -> ([Block] -> m a) + -> ([Inline] -> m a) -> Meta -> m Value metaToJSON opts blockWriter inlineWriter meta @@ -75,9 +75,9 @@ metaToJSON opts blockWriter inlineWriter meta -- | Like 'metaToJSON', but does not include variables and is -- not sensitive to 'writerTemplate'. -metaToJSON' :: Monad m - => ([Block] -> m String) - -> ([Inline] -> m String) +metaToJSON' :: (Monad m, ToJSON a) + => ([Block] -> m a) + -> ([Inline] -> m a) -> Meta -> m Value metaToJSON' blockWriter inlineWriter (Meta metamap) = do @@ -98,9 +98,9 @@ addVariablesToJSON opts metadata = where combineMetadata (Object o1) (Object o2) = Object $ H.union o1 o2 combineMetadata x _ = x -metaValueToJSON :: Monad m - => ([Block] -> m String) - -> ([Inline] -> m String) +metaValueToJSON :: (Monad m, ToJSON a) + => ([Block] -> m a) + -> ([Inline] -> m a) -> MetaValue -> m Value metaValueToJSON blockWriter inlineWriter (MetaMap metamap) = liftM toJSON $ diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 0e1a0526d..27d26c7d9 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -31,6 +31,7 @@ Conversion of 'Pandoc' documents to Docbook XML. -} module Text.Pandoc.Writers.TEI (writeTEI) where import Data.Char (toLower) +import Data.Text (Text) import Data.List (isPrefixOf, stripPrefix) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) @@ -56,12 +57,13 @@ authorToTEI opts name' = do inTagsSimple "author" (text $ escapeStringForXML name) -- | Convert Pandoc document to string in Docbook format. -writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTEI opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + render' :: Doc -> Text render' = render colwidth startLvl = case writerTopLevelDivision opts of TopLevelPart -> -1 @@ -71,9 +73,9 @@ writeTEI opts (Pandoc meta blocks) = do auths' <- mapM (authorToTEI opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . - (mapM (elementToTEI opts startLvl)) . hierarchicalize) - (fmap (render colwidth) . inlinesToTEI opts) + (fmap (render' . vcat) . + mapM (elementToTEI opts startLvl) . hierarchicalize) + (fmap render' . inlinesToTEI opts) meta' main <- (render' . vcat) <$> mapM (elementToTEI opts startLvl) elements let context = defField "body" main diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index da4f43ee5..387e55290 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2008-2015 John MacFarlane and Peter Wang +Copyright (C) 2008-2017 John MacFarlane + 2012 Peter Wang 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 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Texinfo - Copyright : Copyright (C) 2008-2015 John MacFarlane and Peter Wang + Copyright : Copyright (C) 2008-2017 John MacFarlane + 2012 Peter Wang License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -35,7 +37,8 @@ import Data.Char (chr, ord) import Data.List (maximumBy, transpose) import Data.Ord (comparing) import qualified Data.Set as Set -import Network.URI (isURI, unEscapeString) +import Data.Text (Text) +import Network.URI (unEscapeString) import System.FilePath import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -66,7 +69,7 @@ data WriterState = type TI m = StateT WriterState m -- | Convert Pandoc to Texinfo. -writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTexinfo options document = evalStateT (pandocToTexinfo options $ wrapTop document) $ WriterState { stStrikeout = False, stSuperscript = False, @@ -78,16 +81,18 @@ wrapTop :: Pandoc -> Pandoc wrapTop (Pandoc meta blocks) = Pandoc meta (Header 0 nullAttr (docTitle meta) : blocks) -pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m String +pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m Text pandocToTexinfo options (Pandoc meta blocks) = do let titlePage = not $ all null $ docTitle meta : docDate meta : docAuthors meta let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToTexinfo) - (fmap (render colwidth) . inlineListToTexinfo) + (fmap render' . blockListToTexinfo) + (fmap render' . inlineListToTexinfo) meta main <- blockListToTexinfo blocks st <- get diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 0ecb746c3..091a5baca 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Textile - Copyright : Copyright (C) 2010-2015 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -33,6 +33,7 @@ module Text.Pandoc.Writers.Textile ( writeTextile ) where import Control.Monad.State import Data.Char (isSpace) import Data.List (intercalate) +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -54,7 +55,7 @@ data WriterState = WriterState { type TW = StateT WriterState -- | Convert Pandoc to Textile. -writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTextile opts document = evalStateT (pandocToTextile opts document) WriterState { stNotes = [], @@ -64,17 +65,17 @@ writeTextile opts document = -- | Return Textile representation of document. pandocToTextile :: PandocMonad m - => WriterOptions -> Pandoc -> TW m String + => WriterOptions -> Pandoc -> TW m Text pandocToTextile opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (blockListToTextile opts) (inlineListToTextile opts) meta body <- blockListToTextile opts blocks notes <- gets $ unlines . reverse . stNotes - let main = body ++ if null notes then "" else ("\n\n" ++ notes) + let main = pack $ body ++ if null notes then "" else ("\n\n" ++ notes) let context = defField "body" main metadata case writerTemplate opts of - Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Nothing -> return main + Just tpl -> return $ renderTemplate' tpl context withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index da8b08de1..5ee239e59 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -1,5 +1,6 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> + 2017 Alex Ivkin 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 @@ -18,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ZimWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane, 2017 Alex Ivkin + Copyright : Copyright (C) 2008-2017 John MacFarlane, 2017 Alex Ivkin License : GNU GPL, version 2 or above Maintainer : Alex Ivkin <alex@ivkin.net> @@ -36,15 +37,14 @@ import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map -import Data.Text (breakOnAll, pack) -import Network.URI (isURI) +import Data.Text (breakOnAll, pack, Text) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) -import Text.Pandoc.Shared (escapeURI, linesToPara, removeFormatting, substitute, - trimr) +import Text.Pandoc.Shared (isURI, escapeURI, linesToPara, removeFormatting, + substitute, trimr) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) @@ -61,17 +61,17 @@ instance Default WriterState where type ZW = StateT WriterState -- | Convert Pandoc to ZimWiki. -writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeZimWiki opts document = evalStateT (pandocToZimWiki opts document) def -- | Return ZimWiki representation of document. -pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m String +pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m Text pandocToZimWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToZimWiki opts) (inlineListToZimWiki opts) meta - body <- blockListToZimWiki opts blocks + body <- pack <$> blockListToZimWiki opts blocks --let header = "Content-Type: text/x-zim-wiki\nWiki-Format: zim 0.4\n" let main = body let context = defField "body" main diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index d7fdc4278..67608fb43 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.XML - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -37,6 +37,8 @@ module Text.Pandoc.XML ( escapeCharForXML, fromEntities ) where import Data.Char (isAscii, isSpace, ord) +import Data.Text (Text) +import qualified Data.Text as T import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Pretty @@ -91,11 +93,10 @@ inTagsIndented :: String -> Doc -> Doc inTagsIndented tagType = inTags True tagType [] -- | Escape all non-ascii characters using numerical entities. -toEntities :: String -> String -toEntities [] = "" -toEntities (c:cs) - | isAscii c = c : toEntities cs - | otherwise = "&#" ++ show (ord c) ++ ";" ++ toEntities cs +toEntities :: Text -> Text +toEntities = T.concatMap go + where go c | isAscii c = T.singleton c + | otherwise = T.pack ("&#" ++ show (ord c) ++ ";") -- Unescapes XML entities fromEntities :: String -> String diff --git a/stack.full.yaml b/stack.full.yaml index f05ccbce3..e5fff5a4e 100644 --- a/stack.full.yaml +++ b/stack.full.yaml @@ -3,7 +3,6 @@ flags: pandoc: trypandoc: false - https: true embed_data_files: false old-locale: false network-uri: true diff --git a/stack.pkg.yaml b/stack.pkg.yaml index 864982278..fe5103ecf 100644 --- a/stack.pkg.yaml +++ b/stack.pkg.yaml @@ -1,7 +1,6 @@ flags: pandoc: trypandoc: false - https: true embed_data_files: true old-locale: false network-uri: true @@ -15,9 +14,9 @@ packages: - '.' - location: git: https://github.com/jgm/pandoc-citeproc.git - commit: 0926289c5ccc1c69c90b2e9132b0e2b57f08aa4d + commit: 2e27f5cb40577c9b3ffe0fc112687084f3d9d877 extra-dep: false extra-deps: - hslua-0.5.0 - skylighting-0.3.3 -resolver: lts-8.12 +resolver: lts-8.16 diff --git a/stack.yaml b/stack.yaml index b9f02b364..d9c6b6714 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,7 +1,6 @@ flags: pandoc: trypandoc: false - https: true embed_data_files: false old-locale: false network-uri: true @@ -10,4 +9,4 @@ packages: extra-deps: - hslua-0.5.0 - skylighting-0.3.3 -resolver: lts-8.12 +resolver: lts-8.16 diff --git a/test/Tests/Command.hs b/test/Tests/Command.hs index 054ceb50d..1f3694f60 100644 --- a/test/Tests/Command.hs +++ b/test/Tests/Command.hs @@ -13,6 +13,7 @@ import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc import Text.Pandoc.Shared (trimr) +import qualified Data.ByteString as BS import qualified Text.Pandoc.UTF8 as UTF8 import System.IO.Unsafe (unsafePerformIO) -- TODO temporary @@ -23,7 +24,7 @@ runTest :: String -- ^ Title of test -> String -- ^ Expected output -> TestTree runTest testname cmd inp norm = testCase testname $ do - let cmd' = cmd ++ " --quiet --data-dir ../data" + let cmd' = cmd ++ " --data-dir ../data" let findDynlibDir [] = Nothing findDynlibDir ("build":xs) = Just $ joinPath (reverse xs) </> "build" findDynlibDir (_:xs) = findDynlibDir xs @@ -35,9 +36,9 @@ runTest testname cmd inp norm = testCase testname $ do ("LD_LIBRARY_PATH", d)] let env' = dynlibEnv ++ [("TMP","."),("LANG","en_US.UTF-8"),("HOME", "./")] let pr = (shell cmd'){ env = Just env' } - (ec, out', _err) <- readCreateProcessWithExitCode pr inp + (ec, out', err') <- readCreateProcessWithExitCode pr inp -- filter \r so the tests will work on Windows machines - let out = filter (/= '\r') out' + let out = filter (/= '\r') $ err' ++ out' result <- if ec == ExitSuccess then do if out == norm @@ -83,7 +84,7 @@ runCommandTest pandocpath (num, code) = extractCommandTest :: FilePath -> FilePath -> TestTree extractCommandTest pandocpath fp = unsafePerformIO $ do - contents <- UTF8.readFile ("command" </> fp) + contents <- UTF8.toText <$> BS.readFile ("command" </> fp) Pandoc _ blocks <- runIOorExplode (readMarkdown def{ readerExtensions = pandocExtensions } contents) let codeblocks = map extractCode $ filter isCodeBlock $ blocks diff --git a/test/Tests/Helpers.hs b/test/Tests/Helpers.hs index 7e8ebb01a..2a6543ea0 100644 --- a/test/Tests/Helpers.hs +++ b/test/Tests/Helpers.hs @@ -15,6 +15,7 @@ module Tests.Helpers ( test import Data.Algorithm.Diff import qualified Data.Map as M +import Data.Text (Text, unpack) import System.Directory import System.Environment.Executable (getExecutablePath) import System.Exit @@ -105,21 +106,25 @@ class ToString a where toString :: a -> String instance ToString Pandoc where - toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d + toString d = unpack $ + purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of (Pandoc (Meta m) _) | M.null m -> Nothing | otherwise -> Just "" -- need this to get meta output instance ToString Blocks where - toString = purely (writeNative def) . toPandoc + toString = unpack . purely (writeNative def) . toPandoc instance ToString Inlines where - toString = trimr . purely (writeNative def) . toPandoc + toString = trimr . unpack . purely (writeNative def) . toPandoc instance ToString String where toString = id +instance ToString Text where + toString = unpack + class ToPandoc a where toPandoc :: a -> Pandoc diff --git a/test/Tests/Lua.hs b/test/Tests/Lua.hs index 50f9634a2..cd8604ab9 100644 --- a/test/Tests/Lua.hs +++ b/test/Tests/Lua.hs @@ -3,9 +3,9 @@ module Tests.Lua ( tests ) where import Control.Monad (when) import System.FilePath ((</>)) -import Test.Tasty (TestTree) +import Test.Tasty (TestTree, localOption) import Test.Tasty.HUnit (Assertion, assertEqual, testCase) -import Test.Tasty.QuickCheck (ioProperty, testProperty) +import Test.Tasty.QuickCheck (ioProperty, testProperty, QuickCheckTests(..)) import Text.Pandoc.Arbitrary () import Text.Pandoc.Definition (Block, Inline, Meta, Pandoc) import Text.Pandoc.Builder ( (<>), bulletList, doc, doubleQuoted, emph @@ -16,7 +16,7 @@ import Text.Pandoc.Lua import qualified Scripting.Lua as Lua tests :: [TestTree] -tests = +tests = map (localOption (QuickCheckTests 20)) [ testProperty "inline elements can be round-tripped through the lua stack" $ \x -> ioProperty (roundtripEqual (x::Inline)) diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 028a4ff2f..e55c3529b 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -2,11 +2,14 @@ module Tests.Readers.Docx (tests) where import Codec.Archive.Zip import qualified Data.ByteString.Lazy as B +import qualified Data.ByteString as BS +import qualified Data.Text as T import qualified Data.Map as M import Test.Tasty import Test.Tasty.HUnit import Tests.Helpers import Text.Pandoc +import Text.Pandoc.UTF8 as UTF8 import qualified Text.Pandoc.Class as P import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) import System.IO.Unsafe -- TODO temporary @@ -25,7 +28,7 @@ defopts :: ReaderOptions defopts = def{ readerExtensions = getDefaultExtensions "docx" } instance ToString NoNormPandoc where - toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d + toString d = T.unpack $ purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of NoNormPandoc (Pandoc (Meta m) _) | M.null m -> Nothing @@ -40,7 +43,7 @@ compareOutput :: ReaderOptions -> IO (NoNormPandoc, NoNormPandoc) compareOutput opts docxFile nativeFile = do df <- B.readFile docxFile - nf <- Prelude.readFile nativeFile + nf <- UTF8.toText <$> BS.readFile nativeFile p <- runIOorExplode $ readDocx opts df df' <- runIOorExplode $ readNative def nf return $ (noNorm p, noNorm df') diff --git a/test/Tests/Readers/HTML.hs b/test/Tests/Readers/HTML.hs index e2262d131..8647540b6 100644 --- a/test/Tests/Readers/HTML.hs +++ b/test/Tests/Readers/HTML.hs @@ -6,8 +6,9 @@ import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder +import Data.Text (Text) -html :: String -> Pandoc +html :: Text -> Pandoc html = purely $ readHtml def tests :: [TestTree] diff --git a/test/Tests/Readers/LaTeX.hs b/test/Tests/Readers/LaTeX.hs index 75547ed6b..afac9e8cb 100644 --- a/test/Tests/Readers/LaTeX.hs +++ b/test/Tests/Readers/LaTeX.hs @@ -6,14 +6,16 @@ import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder +import Data.Text (Text) +import qualified Data.Text as T -latex :: String -> Pandoc +latex :: Text -> Pandoc latex = purely $ readLaTeX def{ readerExtensions = getDefaultExtensions "latex" } infix 4 =: (=:) :: ToString c - => String -> (String, c) -> TestTree + => String -> (Text, c) -> TestTree (=:) = test latex simpleTable' :: [Alignment] -> [[Blocks]] -> Blocks @@ -74,7 +76,7 @@ tests = [ testGroup "basic" "\\begin{tabular}{|rl|}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] , "Multi line table" =: - unlines [ "\\begin{tabular}{|c|}" + T.unlines [ "\\begin{tabular}{|c|}" , "One\\\\" , "Two\\\\" , "Three\\\\" @@ -91,7 +93,7 @@ tests = [ testGroup "basic" "\\begin{tabular}{@{}r@{}l}One & Two\\\\ \\end{tabular}" =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] , "Table with custom column separators" =: - unlines [ "\\begin{tabular}{@{($\\to$)}r@{\\hspace{2cm}}l}" + T.unlines [ "\\begin{tabular}{@{($\\to$)}r@{\\hspace{2cm}}l}" , "One&Two\\\\" , "\\end{tabular}" ] =?> simpleTable' [AlignRight,AlignLeft] [[plain "One", plain "Two"]] @@ -108,10 +110,10 @@ tests = [ testGroup "basic" , let hex = ['0'..'9']++['a'..'f'] in testGroup "Character Escapes" [ "Two-character escapes" =: - concat ["^^"++[i,j] | i <- hex, j <- hex] =?> + mconcat ["^^" <> T.pack [i,j] | i <- hex, j <- hex] =?> para (str ['\0'..'\255']) , "One-character escapes" =: - concat ["^^"++[i] | i <- hex] =?> + mconcat ["^^" <> T.pack [i] | i <- hex] =?> para (str $ ['p'..'y']++['!'..'&']) ] ] diff --git a/test/Tests/Readers/Markdown.hs b/test/Tests/Readers/Markdown.hs index e1d0c8e1f..1cd32b87d 100644 --- a/test/Tests/Readers/Markdown.hs +++ b/test/Tests/Readers/Markdown.hs @@ -1,38 +1,40 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Readers.Markdown (tests) where +import Data.Text (Text, unpack) +import qualified Data.Text as T import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder -markdown :: String -> Pandoc +markdown :: Text -> Pandoc markdown = purely $ readMarkdown def { readerExtensions = disableExtension Ext_smart pandocExtensions } -markdownSmart :: String -> Pandoc +markdownSmart :: Text -> Pandoc markdownSmart = purely $ readMarkdown def { readerExtensions = enableExtension Ext_smart pandocExtensions } -markdownCDL :: String -> Pandoc +markdownCDL :: Text -> Pandoc markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension Ext_compact_definition_lists pandocExtensions } -markdownGH :: String -> Pandoc +markdownGH :: Text -> Pandoc markdownGH = purely $ readMarkdown def { readerExtensions = githubMarkdownExtensions } infix 4 =: (=:) :: ToString c - => String -> (String, c) -> TestTree + => String -> (Text, c) -> TestTree (=:) = test markdown -testBareLink :: (String, Inlines) -> TestTree +testBareLink :: (Text, Inlines) -> TestTree testBareLink (inp, ils) = test (purely $ readMarkdown def{ readerExtensions = extensionsFromList [Ext_autolink_bare_uris, Ext_raw_html] }) - inp (inp, doc $ para ils) + (unpack inp) (inp, doc $ para ils) autolink :: String -> Inlines autolink = autolinkWith nullAttr @@ -40,7 +42,7 @@ autolink = autolinkWith nullAttr autolinkWith :: Attr -> String -> Inlines autolinkWith attr s = linkWith attr s "" (str s) -bareLinkTests :: [(String, Inlines)] +bareLinkTests :: [(Text, Inlines)] bareLinkTests = [ ("http://google.com is a search engine.", autolink "http://google.com" <> " is a search engine.") @@ -376,10 +378,10 @@ tests = [ testGroup "inline code" rawBlock "html" "</button>" <> divWith nullAttr (para $ text "with this div too.")] , test markdownGH "issue #1636" $ - unlines [ "* a" - , "* b" - , "* c" - , " * d" ] + T.unlines [ "* a" + , "* b" + , "* c" + , " * d" ] =?> bulletList [ plain "a" , plain "b" @@ -419,9 +421,9 @@ tests = [ testGroup "inline code" , let citation = cite [Citation "cita" [] [] AuthorInText 0 0] (str "@cita") in testGroup "footnote/link following citation" -- issue #2083 [ "footnote" =: - unlines [ "@cita[^note]" - , "" - , "[^note]: note" ] =?> + T.unlines [ "@cita[^note]" + , "" + , "[^note]: note" ] =?> para ( citation <> note (para $ str "note") ) @@ -431,22 +433,22 @@ tests = [ testGroup "inline code" citation <> space <> link "http://www.com" "" (str "link") ) , "reference link" =: - unlines [ "@cita [link][link]" - , "" - , "[link]: http://www.com" ] =?> + T.unlines [ "@cita [link][link]" + , "" + , "[link]: http://www.com" ] =?> para ( citation <> space <> link "http://www.com" "" (str "link") ) , "short reference link" =: - unlines [ "@cita [link]" - , "" - , "[link]: http://www.com" ] =?> + T.unlines [ "@cita [link]" + , "" + , "[link]: http://www.com" ] =?> para ( citation <> space <> link "http://www.com" "" (str "link") ) , "implicit header link" =: - unlines [ "# Header" - , "@cita [Header]" ] =?> + T.unlines [ "# Header" + , "@cita [Header]" ] =?> headerWith ("header",[],[]) 1 (str "Header") <> para ( citation <> space <> link "#header" "" (str "Header") ) diff --git a/test/Tests/Readers/Odt.hs b/test/Tests/Readers/Odt.hs index 6fc062158..eed3a33b0 100644 --- a/test/Tests/Readers/Odt.hs +++ b/test/Tests/Readers/Odt.hs @@ -2,7 +2,10 @@ module Tests.Readers.Odt (tests) where import Control.Monad (liftM) import qualified Data.ByteString.Lazy as B +import qualified Data.ByteString as BS +import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.Map as M +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -39,7 +42,8 @@ newtype NoNormPandoc = NoNormPandoc {unNoNorm :: Pandoc} deriving ( Show ) instance ToString NoNormPandoc where - toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d + toString d = unpack $ + purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of NoNormPandoc (Pandoc (Meta m) _) | M.null m -> Nothing @@ -58,7 +62,7 @@ type TestCreator = ReaderOptions compareOdtToNative :: TestCreator compareOdtToNative opts odtPath nativePath = do - nativeFile <- Prelude.readFile nativePath + nativeFile <- UTF8.toText <$> BS.readFile nativePath odtFile <- B.readFile odtPath native <- getNoNormVia id "native" <$> runIO (readNative def nativeFile) odt <- getNoNormVia id "odt" <$> runIO (readOdt opts odtFile) @@ -66,7 +70,7 @@ compareOdtToNative opts odtPath nativePath = do compareOdtToMarkdown :: TestCreator compareOdtToMarkdown opts odtPath markdownPath = do - markdownFile <- Prelude.readFile markdownPath + markdownFile <- UTF8.toText <$> BS.readFile markdownPath odtFile <- B.readFile odtPath markdown <- getNoNormVia id "markdown" <$> runIO (readMarkdown def{ readerExtensions = pandocExtensions } diff --git a/test/Tests/Readers/Org.hs b/test/Tests/Readers/Org.hs index 7a7960396..45b10da42 100644 --- a/test/Tests/Readers/Org.hs +++ b/test/Tests/Readers/Org.hs @@ -2,21 +2,23 @@ module Tests.Readers.Org (tests) where import Data.List (intersperse) +import Data.Text (Text) +import qualified Data.Text as T import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Builder -org :: String -> Pandoc +org :: Text -> Pandoc org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } -orgSmart :: String -> Pandoc +orgSmart :: Text -> Pandoc orgSmart = purely $ readOrg def { readerExtensions = enableExtension Ext_smart $ getDefaultExtensions "org" } infix 4 =: (=:) :: ToString c - => String -> (String, c) -> TestTree + => String -> (Text, c) -> TestTree (=:) = test org spcSep :: [Inlines] -> Inlines @@ -26,7 +28,11 @@ simpleTable' :: Int -> [Blocks] -> [[Blocks]] -> Blocks -simpleTable' n = table "" (take n $ repeat (AlignDefault, 0.0)) +simpleTable' n = table "" (replicate n (AlignDefault, 0.0)) + +-- | Create a span for the given tag. +tagSpan :: String -> Inlines +tagSpan t = spanWith ("", ["tag"], [("data-tag-name", t)]) . smallcaps $ str t tests :: [TestTree] tests = @@ -108,17 +114,17 @@ tests = para (note $ para "Schreib mir eine E-Mail") , "Markup-chars not occuring on word break are symbols" =: - unlines [ "this+that+ +so+on" - , "seven*eight* nine*" - , "+not+funny+" - ] =?> + T.unlines [ "this+that+ +so+on" + , "seven*eight* nine*" + , "+not+funny+" + ] =?> para ("this+that+ +so+on" <> softbreak <> "seven*eight* nine*" <> softbreak <> strikeout "not+funny") , "No empty markup" =: - "// ** __ ++ == ~~ $$" =?> - para (spcSep [ "//", "**", "__", "++", "==", "~~", "$$" ]) + "// ** __ <> == ~~ $$" =?> + para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) , "Adherence to Org's rules for markup borders" =: "/t/& a/ / ./r/ (*l*) /e/! /b/." =?> @@ -139,11 +145,11 @@ tests = para "/nada,/" , "Markup should work properly after a blank line" =: - unlines ["foo", "", "/bar/"] =?> + T.unlines ["foo", "", "/bar/"] =?> (para $ text "foo") <> (para $ emph $ text "bar") , "Inline math must stay within three lines" =: - unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> + T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> para ((math "a\nb\nc") <> softbreak <> "$d" <> softbreak <> "e" <> softbreak <> "f" <> softbreak <> "g$") @@ -165,17 +171,17 @@ tests = softbreak <> "emph/") , "Sub- and superscript expressions" =: - unlines [ "a_(a(b)(c)d)" - , "e^(f(g)h)" - , "i_(jk)l)" - , "m^()n" - , "o_{p{q{}r}}" - , "s^{t{u}v}" - , "w_{xy}z}" - , "1^{}2" - , "3_{{}}" - , "4^(a(*b(c*)d))" - ] =?> + T.unlines [ "a_(a(b)(c)d)" + , "e^(f(g)h)" + , "i_(jk)l)" + , "m^()n" + , "o_{p{q{}r}}" + , "s^{t{u}v}" + , "w_{xy}z}" + , "1^{}2" + , "3_{{}}" + , "4^(a(*b(c*)d))" + ] =?> para (mconcat $ intersperse softbreak [ "a" <> subscript "(a(b)(c)d)" , "e" <> superscript "(f(g)h)" @@ -202,17 +208,17 @@ tests = (para $ image "sunrise.jpg" "" "") , "Multiple images within a paragraph" =: - unlines [ "[[file:sunrise.jpg]]" - , "[[file:sunset.jpg]]" - ] =?> + T.unlines [ "[[file:sunrise.jpg]]" + , "[[file:sunset.jpg]]" + ] =?> (para $ (image "sunrise.jpg" "" "") <> softbreak <> (image "sunset.jpg" "" "")) , "Image with html attributes" =: - unlines [ "#+ATTR_HTML: :width 50%" - , "[[file:guinea-pig.gif]]" - ] =?> + T.unlines [ "#+ATTR_HTML: :width 50%" + , "[[file:guinea-pig.gif]]" + ] =?> (para $ imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") ] @@ -334,6 +340,18 @@ tests = } in (para $ cite [citation] "cite:pandoc") + , "Org-ref simple citation with underscores" =: + "cite:pandoc_org_ref" =?> + let citation = Citation + { citationId = "pandoc_org_ref" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc_org_ref") + , "Org-ref simple citation succeeded by comma" =: "cite:pandoc," =?> let citation = Citation @@ -346,6 +364,30 @@ tests = } in (para $ cite [citation] "cite:pandoc" <> str ",") + , "Org-ref simple citation succeeded by dot" =: + "cite:pandoc." =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc" <> str ".") + + , "Org-ref simple citation succeeded by colon" =: + "cite:pandoc:" =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc" <> str ":") + , "Org-ref simple citep citation" =: "citep:pandoc" =?> let citation = Citation @@ -469,6 +511,24 @@ tests = , citationNoteNum = 0 , citationHash = 0} in (para . cite [citation] $ rawInline "latex" "\\cite{Coffee}") + + , "Macro" =: + T.unlines [ "#+MACRO: HELLO /Hello, $1/" + , "{{{HELLO(World)}}}" + ] =?> + para (emph "Hello, World") + + , "Macro repeting its argument" =: + T.unlines [ "#+MACRO: HELLO $1$1" + , "{{{HELLO(moin)}}}" + ] =?> + para "moinmoin" + + , "Macro called with too few arguments" =: + T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" + , "{{{HELLO()}}}" + ] =?> + para "Foo Bar" ] , testGroup "Meta Information" $ @@ -481,10 +541,10 @@ tests = para "#-tag" , "Comment surrounded by Text" =: - unlines [ "Before" - , "# Comment" - , "After" - ] =?> + T.unlines [ "Before" + , "# Comment" + , "After" + ] =?> mconcat [ para "Before" , para "After" ] @@ -521,10 +581,10 @@ tests = in Pandoc meta mempty , "Properties drawer" =: - unlines [ " :PROPERTIES:" - , " :setting: foo" - , " :END:" - ] =?> + T.unlines [ " :PROPERTIES:" + , " :setting: foo" + , " :END:" + ] =?> (mempty::Blocks) , "LaTeX_headers options are translated to header-includes" =: @@ -552,46 +612,46 @@ tests = in Pandoc meta mempty , "later meta definitions take precedence" =: - unlines [ "#+AUTHOR: this will not be used" - , "#+author: Max" - ] =?> + T.unlines [ "#+AUTHOR: this will not be used" + , "#+author: Max" + ] =?> let author = MetaInlines [Str "Max"] meta = setMeta "author" (MetaList [author]) $ nullMeta in Pandoc meta mempty , "Logbook drawer" =: - unlines [ " :LogBook:" - , " - State \"DONE\" from \"TODO\" [2014-03-03 Mon 11:00]" - , " :END:" - ] =?> + T.unlines [ " :LogBook:" + , " - State \"DONE\" from \"TODO\" [2014-03-03 Mon 11:00]" + , " :END:" + ] =?> (mempty::Blocks) , "Drawer surrounded by text" =: - unlines [ "Before" - , ":PROPERTIES:" - , ":END:" - , "After" - ] =?> + T.unlines [ "Before" + , ":PROPERTIES:" + , ":END:" + , "After" + ] =?> para "Before" <> para "After" , "Drawer markers must be the only text in the line" =: - unlines [ " :LOGBOOK: foo" - , " :END: bar" - ] =?> + T.unlines [ " :LOGBOOK: foo" + , " :END: bar" + ] =?> para (":LOGBOOK: foo" <> softbreak <> ":END: bar") , "Drawers can be arbitrary" =: - unlines [ ":FOO:" - , "/bar/" - , ":END:" - ] =?> + T.unlines [ ":FOO:" + , "/bar/" + , ":END:" + ] =?> divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") , "Anchor reference" =: - unlines [ "<<link-here>> Target." - , "" - , "[[link-here][See here!]]" - ] =?> + T.unlines [ "<<link-here>> Target." + , "" + , "[[link-here][See here!]]" + ] =?> (para (spanWith ("link-here", [], []) mempty <> "Target.") <> para (link "#link-here" "" ("See" <> space <> "here!"))) @@ -600,129 +660,148 @@ tests = (para (emph $ "Where's" <> space <> "Wally?")) , "Link to nonexistent anchor" =: - unlines [ "<<link-here>> Target." - , "" - , "[[link$here][See here!]]" - ] =?> + T.unlines [ "<<link-here>> Target." + , "" + , "[[link$here][See here!]]" + ] =?> (para (spanWith ("link-here", [], []) mempty <> "Target.") <> para (emph ("See" <> space <> "here!"))) , "Link abbreviation" =: - unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" - , "[[wp:Org_mode][Wikipedia on Org-mode]]" - ] =?> + T.unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" + , "[[wp:Org_mode][Wikipedia on Org-mode]]" + ] =?> (para (link "https://en.wikipedia.org/wiki/Org_mode" "" ("Wikipedia" <> space <> "on" <> space <> "Org-mode"))) , "Link abbreviation, defined after first use" =: - unlines [ "[[zl:non-sense][Non-sense articles]]" - , "#+LINK: zl http://zeitlens.com/tags/%s.html" - ] =?> + T.unlines [ "[[zl:non-sense][Non-sense articles]]" + , "#+LINK: zl http://zeitlens.com/tags/%s.html" + ] =?> (para (link "http://zeitlens.com/tags/non-sense.html" "" ("Non-sense" <> space <> "articles"))) , "Link abbreviation, URL encoded arguments" =: - unlines [ "#+link: expl http://example.com/%h/foo" - , "[[expl:Hello, World!][Moin!]]" - ] =?> + T.unlines [ "#+link: expl http://example.com/%h/foo" + , "[[expl:Hello, World!][Moin!]]" + ] =?> (para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!")) , "Link abbreviation, append arguments" =: - unlines [ "#+link: expl http://example.com/" - , "[[expl:foo][bar]]" - ] =?> + T.unlines [ "#+link: expl http://example.com/" + , "[[expl:foo][bar]]" + ] =?> (para (link "http://example.com/foo" "" "bar")) , testGroup "export options" [ "disable simple sub/superscript syntax" =: - unlines [ "#+OPTIONS: ^:nil" - , "a^b" - ] =?> + T.unlines [ "#+OPTIONS: ^:nil" + , "a^b" + ] =?> para "a^b" , "directly select drawers to be exported" =: - unlines [ "#+OPTIONS: d:(\"IMPORTANT\")" - , ":IMPORTANT:" - , "23" - , ":END:" - , ":BORING:" - , "very boring" - , ":END:" - ] =?> + T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")" + , ":IMPORTANT:" + , "23" + , ":END:" + , ":BORING:" + , "very boring" + , ":END:" + ] =?> divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23") , "exclude drawers from being exported" =: - unlines [ "#+OPTIONS: d:(not \"BORING\")" - , ":IMPORTANT:" - , "5" - , ":END:" - , ":BORING:" - , "very boring" - , ":END:" - ] =?> + T.unlines [ "#+OPTIONS: d:(not \"BORING\")" + , ":IMPORTANT:" + , "5" + , ":END:" + , ":BORING:" + , "very boring" + , ":END:" + ] =?> divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5") , "don't include archive trees" =: - unlines [ "#+OPTIONS: arch:nil" - , "* old :ARCHIVE:" - ] =?> + T.unlines [ "#+OPTIONS: arch:nil" + , "* old :ARCHIVE:" + ] =?> (mempty ::Blocks) , "include complete archive trees" =: - unlines [ "#+OPTIONS: arch:t" - , "* old :ARCHIVE:" - , " boring" - ] =?> - let tagSpan t = spanWith ("", ["tag"], [("data-tag-name", t)]) mempty - in mconcat [ headerWith ("old", [], mempty) 1 ("old" <> tagSpan "ARCHIVE") - , para "boring" - ] + T.unlines [ "#+OPTIONS: arch:t" + , "* old :ARCHIVE:" + , " boring" + ] =?> + mconcat [ headerWith ("old", [], mempty) 1 + ("old" <> space <> tagSpan "ARCHIVE") + , para "boring" + ] , "include archive tree header only" =: - unlines [ "#+OPTIONS: arch:headline" - , "* old :ARCHIVE:" - , " boring" - ] =?> - let tagSpan t = spanWith ("", ["tag"], [("data-tag-name", t)]) mempty - in headerWith ("old", [], mempty) 1 ("old" <> tagSpan "ARCHIVE") + T.unlines [ "#+OPTIONS: arch:headline" + , "* old :ARCHIVE:" + , " boring" + ] =?> + headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE") , "limit headline depth" =: - unlines [ "#+OPTIONS: H:2" - , "* section" - , "** subsection" - , "*** list item 1" - , "*** list item 2" - ] =?> - mconcat [ headerWith ("section", [], []) 1 "section" + T.unlines [ "#+OPTIONS: H:2" + , "* top-level section" + , "** subsection" + , "*** list item 1" + , "*** list item 2" + ] =?> + mconcat [ headerWith ("top-level-section", [], []) 1 "top-level section" , headerWith ("subsection", [], []) 2 "subsection" , orderedList [ para "list item 1", para "list item 2" ] ] + , "turn all headlines into lists" =: + T.unlines [ "#+OPTIONS: H:0" + , "first block" + , "* top-level section 1" + , "** subsection" + , "* top-level section 2" + ] =?> + mconcat [ para "first block" + , orderedList + [ (para "top-level section 1" <> + orderedList [ para "subsection" ]) + , para "top-level section 2" ] + ] + , "disable author export" =: - unlines [ "#+OPTIONS: author:nil" - , "#+AUTHOR: ShyGuy" - ] =?> + T.unlines [ "#+OPTIONS: author:nil" + , "#+AUTHOR: ShyGuy" + ] =?> Pandoc nullMeta mempty , "disable creator export" =: - unlines [ "#+OPTIONS: creator:nil" - , "#+creator: The Architect" - ] =?> + T.unlines [ "#+OPTIONS: creator:nil" + , "#+creator: The Architect" + ] =?> Pandoc nullMeta mempty , "disable email export" =: - unlines [ "#+OPTIONS: email:nil" - , "#+email: no-mail-please@example.com" - ] =?> + T.unlines [ "#+OPTIONS: email:nil" + , "#+email: no-mail-please@example.com" + ] =?> Pandoc nullMeta mempty , "disable inclusion of todo keywords" =: - unlines [ "#+OPTIONS: todo:nil" - , "** DONE todo export" - ] =?> + T.unlines [ "#+OPTIONS: todo:nil" + , "** DONE todo export" + ] =?> headerWith ("todo-export", [], []) 2 "todo export" + + , "remove tags from headlines" =: + T.unlines [ "#+OPTIONS: tags:nil" + , "* Headline :hello:world:" + ] =?> + headerWith ("headline", [], mempty) 1 "Headline" ] ] @@ -743,10 +822,10 @@ tests = ("Third" <> space <> "Level" <> space <> "Headline") , "Compact Headers with Paragraph" =: - unlines [ "* First Level" - , "** Second Level" - , " Text" - ] =?> + T.unlines [ "* First Level" + , "** Second Level" + , " Text" + ] =?> mconcat [ headerWith ("first-level", [], []) 1 ("First" <> space <> "Level") @@ -757,12 +836,12 @@ tests = ] , "Separated Headers with Paragraph" =: - unlines [ "* First Level" - , "" - , "** Second Level" - , "" - , " Text" - ] =?> + T.unlines [ "* First Level" + , "" + , "** Second Level" + , "" + , " Text" + ] =?> mconcat [ headerWith ("first-level", [], []) 1 ("First" <> space <> "Level") @@ -773,10 +852,10 @@ tests = ] , "Headers not preceded by a blank line" =: - unlines [ "** eat dinner" - , "Spaghetti and meatballs tonight." - , "** walk dog" - ] =?> + T.unlines [ "** eat dinner" + , "Spaghetti and meatballs tonight." + , "** walk dog" + ] =?> mconcat [ headerWith ("eat-dinner", [], []) 2 ("eat" <> space <> "dinner") @@ -802,21 +881,21 @@ tests = headerWith ("waiting-header", [], []) 1 "WAITING header" , "Custom todo keywords" =: - unlines [ "#+TODO: WAITING CANCELLED" - , "* WAITING compile" - , "* CANCELLED lunch" - ] =?> + T.unlines [ "#+TODO: WAITING CANCELLED" + , "* WAITING compile" + , "* CANCELLED lunch" + ] =?> let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") , "Custom todo keywords with multiple done-states" =: - unlines [ "#+TODO: WAITING | DONE CANCELLED " - , "* WAITING compile" - , "* CANCELLED lunch" - , "* DONE todo-feature" - ] =?> + T.unlines [ "#+TODO: WAITING | DONE CANCELLED " + , "* WAITING compile" + , "* CANCELLED lunch" + , "* DONE todo-feature" + ] =?> let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" done = spanWith ("", ["done", "DONE"], []) "DONE" @@ -826,31 +905,30 @@ tests = ] , "Tagged headers" =: - unlines [ "* Personal :PERSONAL:" - , "** Call Mom :@PHONE:" - , "** Call John :@PHONE:JOHN: " - ] =?> - let tagSpan t = spanWith ("", ["tag"], [("data-tag-name", t)]) mempty - in mconcat [ headerWith ("personal", [], []) - 1 - ("Personal" <> tagSpan "PERSONAL") - , headerWith ("call-mom", [], []) - 2 - ("Call Mom" <> tagSpan "@PHONE") - , headerWith ("call-john", [], []) - 2 - ("Call John" <> tagSpan "@PHONE" <> tagSpan "JOHN") - ] + T.unlines [ "* Personal :PERSONAL:" + , "** Call Mom :@PHONE:" + , "** Call John :@PHONE:JOHN: " + ] =?> + mconcat [ headerWith ("personal", [], []) + 1 + ("Personal " <> tagSpan "PERSONAL") + , headerWith ("call-mom", [], []) + 2 + ("Call Mom " <> tagSpan "@PHONE") + , headerWith ("call-john", [], []) + 2 + ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") + ] , "Untagged header containing colons" =: "* This: is not: tagged" =?> headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" , "Header starting with strokeout text" =: - unlines [ "foo" - , "" - , "* +thing+ other thing" - ] =?> + T.unlines [ "foo" + , "" + , "* +thing+ other thing" + ] =?> mconcat [ para "foo" , headerWith ("thing-other-thing", [], []) 1 @@ -858,11 +936,11 @@ tests = ] , "Comment Trees" =: - unlines [ "* COMMENT A comment tree" - , " Not much going on here" - , "** This will be dropped" - , "* Comment tree above" - ] =?> + T.unlines [ "* COMMENT A comment tree" + , " Not much going on here" + , "** This will be dropped" + , "* Comment tree above" + ] =?> headerWith ("comment-tree-above", [], []) 1 "Comment tree above" , "Nothing but a COMMENT header" =: @@ -870,38 +948,38 @@ tests = (mempty::Blocks) , "Tree with :noexport:" =: - unlines [ "* Should be ignored :archive:noexport:old:" - , "** Old stuff" - , " This is not going to be exported" - ] =?> + T.unlines [ "* Should be ignored :archive:noexport:old:" + , "** Old stuff" + , " This is not going to be exported" + ] =?> (mempty::Blocks) , "Subtree with :noexport:" =: - unlines [ "* Exported" - , "** This isn't exported :noexport:" - , "*** This neither" - , "** But this is" - ] =?> + T.unlines [ "* Exported" + , "** This isn't exported :noexport:" + , "*** This neither" + , "** But this is" + ] =?> mconcat [ headerWith ("exported", [], []) 1 "Exported" , headerWith ("but-this-is", [], []) 2 "But this is" ] , "Preferences are treated as header attributes" =: - unlines [ "* foo" - , " :PROPERTIES:" - , " :custom_id: fubar" - , " :bar: baz" - , " :END:" - ] =?> + T.unlines [ "* foo" + , " :PROPERTIES:" + , " :custom_id: fubar" + , " :bar: baz" + , " :END:" + ] =?> headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" , "Headers marked with a unnumbered property get a class of the same name" =: - unlines [ "* Not numbered" - , " :PROPERTIES:" - , " :UNNUMBERED: t" - , " :END:" - ] =?> + T.unlines [ "* Not numbered" + , " :PROPERTIES:" + , " :UNNUMBERED: t" + , " :END:" + ] =?> headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" ] , "Paragraph starting with an asterisk" =: @@ -909,23 +987,23 @@ tests = para "*five" , "Paragraph containing asterisk at beginning of line" =: - unlines [ "lucky" - , "*star" - ] =?> + T.unlines [ "lucky" + , "*star" + ] =?> para ("lucky" <> softbreak <> "*star") , "Example block" =: - unlines [ ": echo hello" - , ": echo dear tester" - ] =?> + T.unlines [ ": echo hello" + , ": echo dear tester" + ] =?> codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" , "Example block surrounded by text" =: - unlines [ "Greetings" - , ": echo hello" - , ": echo dear tester" - , "Bye" - ] =?> + T.unlines [ "Greetings" + , ": echo hello" + , ": echo dear tester" + , "Bye" + ] =?> mconcat [ para "Greetings" , codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" @@ -933,81 +1011,81 @@ tests = ] , "Horizontal Rule" =: - unlines [ "before" - , "-----" - , "after" - ] =?> + T.unlines [ "before" + , "-----" + , "after" + ] =?> mconcat [ para "before" , horizontalRule , para "after" ] , "Not a Horizontal Rule" =: - "----- five dashes" =?> - (para $ spcSep [ "-----", "five", "dashes" ]) + "----- em and en dash" =?> + para "\8212\8211 em and en dash" , "Comment Block" =: - unlines [ "#+BEGIN_COMMENT" - , "stuff" - , "bla" - , "#+END_COMMENT"] =?> + T.unlines [ "#+BEGIN_COMMENT" + , "stuff" + , "bla" + , "#+END_COMMENT"] =?> (mempty::Blocks) , testGroup "Figures" $ [ "Figure" =: - unlines [ "#+caption: A very courageous man." - , "#+name: goodguy" - , "[[file:edward.jpg]]" - ] =?> + T.unlines [ "#+caption: A very courageous man." + , "#+name: goodguy" + , "[[file:edward.jpg]]" + ] =?> para (image "edward.jpg" "fig:goodguy" "A very courageous man.") , "Figure with no name" =: - unlines [ "#+caption: I've been through the desert on this" - , "[[file:horse.png]]" - ] =?> + T.unlines [ "#+caption: I've been through the desert on this" + , "[[file:horse.png]]" + ] =?> para (image "horse.png" "fig:" "I've been through the desert on this") , "Figure with `fig:` prefix in name" =: - unlines [ "#+caption: Used as a metapher in evolutionary biology." - , "#+name: fig:redqueen" - , "[[./the-red-queen.jpg]]" - ] =?> + T.unlines [ "#+caption: Used as a metapher in evolutionary biology." + , "#+name: fig:redqueen" + , "[[./the-red-queen.jpg]]" + ] =?> para (image "./the-red-queen.jpg" "fig:redqueen" "Used as a metapher in evolutionary biology.") , "Figure with HTML attributes" =: - unlines [ "#+CAPTION: mah brain just explodid" - , "#+NAME: lambdacat" - , "#+ATTR_HTML: :style color: blue :role button" - , "[[file:lambdacat.jpg]]" - ] =?> + T.unlines [ "#+CAPTION: mah brain just explodid" + , "#+NAME: lambdacat" + , "#+ATTR_HTML: :style color: blue :role button" + , "[[file:lambdacat.jpg]]" + ] =?> let kv = [("style", "color: blue"), ("role", "button")] name = "fig:lambdacat" caption = "mah brain just explodid" in para (imageWith (mempty, mempty, kv) "lambdacat.jpg" name caption) , "Labelled figure" =: - unlines [ "#+CAPTION: My figure" - , "#+LABEL: fig:myfig" - , "[[file:blub.png]]" - ] =?> + T.unlines [ "#+CAPTION: My figure" + , "#+LABEL: fig:myfig" + , "[[file:blub.png]]" + ] =?> let attr = ("fig:myfig", mempty, mempty) in para (imageWith attr "blub.png" "fig:" "My figure") , "Figure with empty caption" =: - unlines [ "#+CAPTION:" - , "[[file:guess.jpg]]" - ] =?> + T.unlines [ "#+CAPTION:" + , "[[file:guess.jpg]]" + ] =?> para (image "guess.jpg" "fig:" "") ] , "Footnote" =: - unlines [ "A footnote[1]" - , "" - , "[1] First paragraph" - , "" - , "second paragraph" - ] =?> + T.unlines [ "A footnote[1]" + , "" + , "[1] First paragraph" + , "" + , "second paragraph" + ] =?> para (mconcat [ "A", space, "footnote" , note $ mconcat [ para ("First" <> space <> "paragraph") @@ -1016,12 +1094,12 @@ tests = ]) , "Two footnotes" =: - unlines [ "Footnotes[fn:1][fn:2]" - , "" - , "[fn:1] First note." - , "" - , "[fn:2] Second note." - ] =?> + T.unlines [ "Footnotes[fn:1][fn:2]" + , "" + , "[fn:1] First note." + , "" + , "[fn:2] Second note." + ] =?> para (mconcat [ "Footnotes" , note $ para ("First" <> space <> "note.") @@ -1029,32 +1107,32 @@ tests = ]) , "Emphasized text before footnote" =: - unlines [ "/text/[fn:1]" - , "" - , "[fn:1] unicorn" - ] =?> + T.unlines [ "/text/[fn:1]" + , "" + , "[fn:1] unicorn" + ] =?> para (mconcat [ emph "text" , note . para $ "unicorn" ]) , "Footnote that starts with emphasized text" =: - unlines [ "text[fn:1]" - , "" - , "[fn:1] /emphasized/" - ] =?> + T.unlines [ "text[fn:1]" + , "" + , "[fn:1] /emphasized/" + ] =?> para (mconcat [ "text" , note . para $ emph "emphasized" ]) , "Footnote followed by header" =: - unlines [ "Another note[fn:yay]" - , "" - , "[fn:yay] This is great!" - , "" - , "** Headline" - ] =?> + T.unlines [ "Another note[fn:yay]" + , "" + , "[fn:yay] This is great!" + , "" + , "** Headline" + ] =?> mconcat [ para (mconcat [ "Another", space, "note" @@ -1066,43 +1144,43 @@ tests = , testGroup "Lists" $ [ "Simple Bullet Lists" =: - ("- Item1\n" ++ + ("- Item1\n" <> "- Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Indented Bullet Lists" =: - (" - Item1\n" ++ + (" - Item1\n" <> " - Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Unindented *" =: - ("- Item1\n" ++ + ("- Item1\n" <> "* Item2\n") =?> bulletList [ plain "Item1" ] <> headerWith ("item2", [], []) 1 "Item2" , "Multi-line Bullet Lists" =: - ("- *Fat\n" ++ - " Tony*\n" ++ - "- /Sideshow\n" ++ + ("- *Fat\n" <> + " Tony*\n" <> + "- /Sideshow\n" <> " Bob/") =?> bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") , plain $ emph ("Sideshow" <> softbreak <> "Bob") ] , "Nested Bullet Lists" =: - ("- Discovery\n" ++ - " + One More Time\n" ++ - " + Harder, Better, Faster, Stronger\n" ++ - "- Homework\n" ++ - " + Around the World\n"++ - "- Human After All\n" ++ - " + Technologic\n" ++ + ("- Discovery\n" <> + " + One More Time\n" <> + " + Harder, Better, Faster, Stronger\n" <> + "- Homework\n" <> + " + Around the World\n"<> + "- Human After All\n" <> + " + Technologic\n" <> " + Robot Rock\n") =?> bulletList [ mconcat [ plain "Discovery" @@ -1158,7 +1236,7 @@ tests = ] , "Simple Ordered List" =: - ("1. Item1\n" ++ + ("1. Item1\n" <> "2. Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" @@ -1167,7 +1245,7 @@ tests = in orderedListWith listStyle listStructure , "Simple Ordered List with Parens" =: - ("1) Item1\n" ++ + ("1) Item1\n" <> "2) Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" @@ -1176,7 +1254,7 @@ tests = in orderedListWith listStyle listStructure , "Indented Ordered List" =: - (" 1. Item1\n" ++ + (" 1. Item1\n" <> " 2. Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" @@ -1185,11 +1263,11 @@ tests = in orderedListWith listStyle listStructure , "Nested Ordered Lists" =: - ("1. One\n" ++ - " 1. One-One\n" ++ - " 2. One-Two\n" ++ - "2. Two\n" ++ - " 1. Two-One\n"++ + ("1. One\n" <> + " 1. One-One\n" <> + " 2. One-Two\n" <> + "2. Two\n" <> + " 1. Two-One\n"<> " 2. Two-Two\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ mconcat @@ -1208,25 +1286,25 @@ tests = in orderedListWith listStyle listStructure , "Ordered List in Bullet List" =: - ("- Emacs\n" ++ + ("- Emacs\n" <> " 1. Org\n") =?> bulletList [ (plain "Emacs") <> (orderedList [ plain "Org"]) ] , "Bullet List in Ordered List" =: - ("1. GNU\n" ++ + ("1. GNU\n" <> " - Freedom\n") =?> orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] , "Definition List" =: - unlines [ "- PLL :: phase-locked loop" - , "- TTL ::" - , " transistor-transistor logic" - , "- PSK :: phase-shift keying" - , "" - , " a digital modulation scheme" - ] =?> + T.unlines [ "- PLL :: phase-locked loop" + , "- TTL ::" + , " transistor-transistor logic" + , "- PSK :: phase-shift keying" + , "" + , " a digital modulation scheme" + ] =?> definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) , ("TTL", [ plain $ "transistor-transistor" <> space <> "logic" ]) @@ -1241,11 +1319,11 @@ tests = " - Elijah Wood :: He plays Frodo" =?> definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] , "Compact definition list" =: - unlines [ "- ATP :: adenosine 5' triphosphate" - , "- DNA :: deoxyribonucleic acid" - , "- PCR :: polymerase chain reaction" - , "" - ] =?> + T.unlines [ "- ATP :: adenosine 5' triphosphate" + , "- DNA :: deoxyribonucleic acid" + , "- PCR :: polymerase chain reaction" + , "" + ] =?> definitionList [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) @@ -1267,21 +1345,21 @@ tests = bulletList [ plain "std::cout" ] , "Loose bullet list" =: - unlines [ "- apple" - , "" - , "- orange" - , "" - , "- peach" - ] =?> + T.unlines [ "- apple" + , "" + , "- orange" + , "" + , "- peach" + ] =?> bulletList [ para "apple" , para "orange" , para "peach" ] , "Recognize preceding paragraphs in non-list contexts" =: - unlines [ "CLOSED: [2015-10-19 Mon 15:03]" - , "- Note taken on [2015-10-19 Mon 13:24]" - ] =?> + T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" + , "- Note taken on [2015-10-19 Mon 13:24]" + ] =?> mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] ] @@ -1297,10 +1375,10 @@ tests = simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , "Multi line table" =: - unlines [ "| One |" - , "| Two |" - , "| Three |" - ] =?> + T.unlines [ "| One |" + , "| Two |" + , "| Three |" + ] =?> simpleTable' 1 mempty [ [ plain "One" ] , [ plain "Two" ] @@ -1312,10 +1390,10 @@ tests = simpleTable' 1 mempty [[mempty]] , "Glider Table" =: - unlines [ "| 1 | 0 | 0 |" - , "| 0 | 1 | 1 |" - , "| 1 | 1 | 0 |" - ] =?> + T.unlines [ "| 1 | 0 | 0 |" + , "| 0 | 1 | 1 |" + , "| 1 | 1 | 0 |" + ] =?> simpleTable' 3 mempty [ [ plain "1", plain "0", plain "0" ] , [ plain "0", plain "1", plain "1" ] @@ -1323,42 +1401,42 @@ tests = ] , "Table between Paragraphs" =: - unlines [ "Before" - , "| One | Two |" - , "After" - ] =?> + T.unlines [ "Before" + , "| One | Two |" + , "After" + ] =?> mconcat [ para "Before" , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , para "After" ] , "Table with Header" =: - unlines [ "| Species | Status |" - , "|--------------+--------------|" - , "| cervisiae | domesticated |" - , "| paradoxus | wild |" - ] =?> + T.unlines [ "| Species | Status |" + , "|--------------+--------------|" + , "| cervisiae | domesticated |" + , "| paradoxus | wild |" + ] =?> simpleTable [ plain "Species", plain "Status" ] [ [ plain "cervisiae", plain "domesticated" ] , [ plain "paradoxus", plain "wild" ] ] , "Table with final hline" =: - unlines [ "| cervisiae | domesticated |" - , "| paradoxus | wild |" - , "|--------------+--------------|" - ] =?> + T.unlines [ "| cervisiae | domesticated |" + , "| paradoxus | wild |" + , "|--------------+--------------|" + ] =?> simpleTable' 2 mempty [ [ plain "cervisiae", plain "domesticated" ] , [ plain "paradoxus", plain "wild" ] ] , "Table in a box" =: - unlines [ "|---------|---------|" - , "| static | Haskell |" - , "| dynamic | Lisp |" - , "|---------+---------|" - ] =?> + T.unlines [ "|---------|---------|" + , "| static | Haskell |" + , "| dynamic | Lisp |" + , "|---------+---------|" + ] =?> simpleTable' 2 mempty [ [ plain "static", plain "Haskell" ] , [ plain "dynamic", plain "Lisp" ] @@ -1369,18 +1447,18 @@ tests = simpleTable' 3 mempty [[mempty, mempty, plain "c"]] , "Table with empty rows" =: - unlines [ "| first |" - , "| |" - , "| third |" - ] =?> + T.unlines [ "| first |" + , "| |" + , "| third |" + ] =?> simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] , "Table with alignment row" =: - unlines [ "| Numbers | Text | More |" - , "| <c> | <r> | |" - , "| 1 | One | foo |" - , "| 2 | Two | bar |" - ] =?> + T.unlines [ "| Numbers | Text | More |" + , "| <c> | <r> | |" + , "| 1 | One | foo |" + , "| 2 | Two | bar |" + ] =?> table "" (zip [AlignCenter, AlignRight, AlignDefault] [0, 0, 0]) [] [ [ plain "Numbers", plain "Text", plain "More" ] @@ -1397,12 +1475,12 @@ tests = simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] , "Table with differing row lengths" =: - unlines [ "| Numbers | Text " - , "|-" - , "| <c> | <r> |" - , "| 1 | One | foo |" - , "| 2" - ] =?> + T.unlines [ "| Numbers | Text " + , "|-" + , "| <c> | <r> |" + , "| 1 | One | foo |" + , "| 2" + ] =?> table "" (zip [AlignCenter, AlignRight] [0, 0]) [ plain "Numbers", plain "Text" ] [ [ plain "1" , plain "One" , plain "foo" ] @@ -1410,10 +1488,10 @@ tests = ] , "Table with caption" =: - unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" - , "| x | 6 |" - , "| 9 | 42 |" - ] =?> + T.unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" + , "| x | 6 |" + , "| 9 | 42 |" + ] =?> table "Hitchhiker's Multiplication Table" [(AlignDefault, 0), (AlignDefault, 0)] [] @@ -1424,59 +1502,59 @@ tests = , testGroup "Blocks and fragments" [ "Source block" =: - unlines [ " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"moin\"" - , " #+END_SRC" ] =?> + T.unlines [ " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"moin\"" + , " #+END_SRC" ] =?> let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" ++ + code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Source block with indented code" =: - unlines [ " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"moin\"" - , " #+END_SRC" ] =?> + T.unlines [ " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"moin\"" + , " #+END_SRC" ] =?> let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" ++ + code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Source block with tab-indented code" =: - unlines [ "\t#+BEGIN_SRC haskell" - , "\tmain = putStrLn greeting" - , "\t where greeting = \"moin\"" - , "\t#+END_SRC" ] =?> + T.unlines [ "\t#+BEGIN_SRC haskell" + , "\tmain = putStrLn greeting" + , "\t where greeting = \"moin\"" + , "\t#+END_SRC" ] =?> let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" ++ + code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlockWith attr' code' , "Empty source block" =: - unlines [ " #+BEGIN_SRC haskell" - , " #+END_SRC" ] =?> + T.unlines [ " #+BEGIN_SRC haskell" + , " #+END_SRC" ] =?> let attr' = ("", ["haskell"], []) code' = "" in codeBlockWith attr' code' , "Source block between paragraphs" =: - unlines [ "Low German greeting" - , " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"Moin!\"" - , " #+END_SRC" ] =?> + T.unlines [ "Low German greeting" + , " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"Moin!\"" + , " #+END_SRC" ] =?> let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" ++ + code' = "main = putStrLn greeting\n" <> " where greeting = \"Moin!\"\n" in mconcat [ para $ spcSep [ "Low", "German", "greeting" ] , codeBlockWith attr' code' ] , "Source block with babel arguments" =: - unlines [ "#+BEGIN_SRC emacs-lisp :exports both" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" ] =?> + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" ] =?> let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax params = [ ("data-org-language", "emacs-lisp") , ("exports", "both") @@ -1486,13 +1564,13 @@ tests = in codeBlockWith ("", classes, params) code' , "Source block with results and :exports both" =: - unlines [ "#+BEGIN_SRC emacs-lisp :exports both" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65"] =?> + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65"] =?> let classes = [ "commonlisp" ] params = [ ("data-org-language", "emacs-lisp") , ("exports", "both") @@ -1505,13 +1583,13 @@ tests = codeBlockWith ("", ["example"], []) results' , "Source block with results and :exports code" =: - unlines [ "#+BEGIN_SRC emacs-lisp :exports code" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports code" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> let classes = [ "commonlisp" ] params = [ ("data-org-language", "emacs-lisp") , ("exports", "code") @@ -1521,87 +1599,87 @@ tests = in codeBlockWith ("", classes, params) code' , "Source block with results and :exports results" =: - unlines [ "#+BEGIN_SRC emacs-lisp :exports results" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports results" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> let results' = "65\n" in codeBlockWith ("", ["example"], []) results' , "Source block with results and :exports none" =: - unlines [ "#+BEGIN_SRC emacs-lisp :exports none" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports none" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> (mempty :: Blocks) , "Source block with toggling header arguments" =: - unlines [ "#+BEGIN_SRC sh :noeval" - , "echo $HOME" - , "#+END_SRC" - ] =?> + T.unlines [ "#+BEGIN_SRC sh :noeval" + , "echo $HOME" + , "#+END_SRC" + ] =?> let classes = [ "bash" ] params = [ ("data-org-language", "sh"), ("noeval", "yes") ] in codeBlockWith ("", classes, params) "echo $HOME\n" , "Source block with line number switch" =: - unlines [ "#+BEGIN_SRC sh -n 10" - , ":() { :|:& };:" - , "#+END_SRC" - ] =?> + T.unlines [ "#+BEGIN_SRC sh -n 10" + , ":() { :|:& };:" + , "#+END_SRC" + ] =?> let classes = [ "bash", "numberLines" ] params = [ ("data-org-language", "sh"), ("startFrom", "10") ] in codeBlockWith ("", classes, params) ":() { :|:& };:\n" , "Source block with multi-word parameter values" =: - unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " - , "digraph { id [label=\"ID\"] }" - , "#+END_SRC" - ] =?> + T.unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " + , "digraph { id [label=\"ID\"] }" + , "#+END_SRC" + ] =?> let classes = [ "dot" ] params = [ ("cmdline", "-Kdot -Tpng") ] in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" , "Example block" =: - unlines [ "#+begin_example" - , "A chosen representation of" - , "a rule." - , "#+eND_exAMPle" - ] =?> + T.unlines [ "#+begin_example" + , "A chosen representation of" + , "a rule." + , "#+eND_exAMPle" + ] =?> codeBlockWith ("", ["example"], []) "A chosen representation of\na rule.\n" , "HTML block" =: - unlines [ "#+BEGIN_HTML" - , "<aside>HTML5 is pretty nice.</aside>" - , "#+END_HTML" - ] =?> + T.unlines [ "#+BEGIN_HTML" + , "<aside>HTML5 is pretty nice.</aside>" + , "#+END_HTML" + ] =?> rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" , "Quote block" =: - unlines [ "#+BEGIN_QUOTE" - , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" - , "#+END_QUOTE" - ] =?> + T.unlines [ "#+BEGIN_QUOTE" + , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" + , "#+END_QUOTE" + ] =?> blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," , "eine", "Mauer", "zu", "errichten!" ])) , "Verse block" =: - unlines [ "The first lines of Goethe's /Faust/:" - , "#+begin_verse" - , "Habe nun, ach! Philosophie," - , "Juristerei und Medizin," - , "Und leider auch Theologie!" - , "Durchaus studiert, mit heißem Bemühn." - , "#+end_verse" - ] =?> + T.unlines [ "The first lines of Goethe's /Faust/:" + , "#+begin_verse" + , "Habe nun, ach! Philosophie," + , "Juristerei und Medizin," + , "Und leider auch Theologie!" + , "Durchaus studiert, mit heißem Bemühn." + , "#+end_verse" + ] =?> mconcat [ para $ spcSep [ "The", "first", "lines", "of" , "Goethe's", emph "Faust" <> ":"] @@ -1614,27 +1692,27 @@ tests = ] , "Verse block with blank lines" =: - unlines [ "#+BEGIN_VERSE" - , "foo" - , "" - , "bar" - , "#+END_VERSE" - ] =?> + T.unlines [ "#+BEGIN_VERSE" + , "foo" + , "" + , "bar" + , "#+END_VERSE" + ] =?> lineBlock [ "foo", mempty, "bar" ] , "Verse block with varying indentation" =: - unlines [ "#+BEGIN_VERSE" - , " hello darkness" - , "my old friend" - , "#+END_VERSE" - ] =?> + T.unlines [ "#+BEGIN_VERSE" + , " hello darkness" + , "my old friend" + , "#+END_VERSE" + ] =?> lineBlock [ "\160\160hello darkness", "my old friend" ] , "Raw block LaTeX" =: - unlines [ "#+BEGIN_LaTeX" - , "The category $\\cat{Set}$ is adhesive." - , "#+END_LaTeX" - ] =?> + T.unlines [ "#+BEGIN_LaTeX" + , "The category $\\cat{Set}$ is adhesive." + , "#+END_LaTeX" + ] =?> rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" , "Raw LaTeX line" =: @@ -1650,24 +1728,24 @@ tests = rawBlock "html" "<aside>not important</aside>" , "Export block HTML" =: - unlines [ "#+BEGIN_export html" - , "<samp>Hello, World!</samp>" - , "#+END_export" - ] =?> + T.unlines [ "#+BEGIN_export html" + , "<samp>Hello, World!</samp>" + , "#+END_export" + ] =?> rawBlock "html" "<samp>Hello, World!</samp>\n" , "LaTeX fragment" =: - unlines [ "\\begin{equation}" - , "X_i = \\begin{cases}" - , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" - , " C_{\\alpha(i)} & \\text{otherwise}" - , " \\end{cases}" - , "\\end{equation}" - ] =?> + T.unlines [ "\\begin{equation}" + , "X_i = \\begin{cases}" + , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" + , " C_{\\alpha(i)} & \\text{otherwise}" + , " \\end{cases}" + , "\\end{equation}" + ] =?> rawBlock "latex" (unlines [ "\\begin{equation}" , "X_i = \\begin{cases}" - , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" ++ + , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" <> " \\alpha(i)\\\\" , " C_{\\alpha(i)} & \\text{otherwise}" , " \\end{cases}" @@ -1675,13 +1753,13 @@ tests = ]) , "Code block with caption" =: - unlines [ "#+CAPTION: Functor laws in Haskell" - , "#+NAME: functor-laws" - , "#+BEGIN_SRC haskell" - , "fmap id = id" - , "fmap (p . q) = (fmap p) . (fmap q)" - , "#+END_SRC" - ] =?> + T.unlines [ "#+CAPTION: Functor laws in Haskell" + , "#+NAME: functor-laws" + , "#+BEGIN_SRC haskell" + , "fmap id = id" + , "fmap (p . q) = (fmap p) . (fmap q)" + , "#+END_SRC" + ] =?> divWith nullAttr (mappend @@ -1693,28 +1771,28 @@ tests = ]))) , "Convert blank lines in blocks to single newlines" =: - unlines [ "#+begin_html" - , "" - , "<span>boring</span>" - , "" - , "#+end_html" - ] =?> + T.unlines [ "#+begin_html" + , "" + , "<span>boring</span>" + , "" + , "#+end_html" + ] =?> rawBlock "html" "\n<span>boring</span>\n\n" , "Accept `ATTR_HTML` attributes for generic block" =: - unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" - , "#+BEGIN_TEST" - , "nonsense" - , "#+END_TEST" - ] =?> + T.unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" + , "#+BEGIN_TEST" + , "nonsense" + , "#+END_TEST" + ] =?> let attr = ("test", ["fun", "code", "TEST"], [("title", "hello, world")]) in divWith attr (para "nonsense") , "Non-letter chars in source block parameters" =: - unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" - , "code body" - , "#+END_SRC" - ] =?> + T.unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" + , "code body" + , "#+END_SRC" + ] =?> let params = [ ("data-org-language", "C") , ("tangle", "xxxx.c") , ("city", "Zürich") diff --git a/test/Tests/Readers/RST.hs b/test/Tests/Readers/RST.hs index 7f67ee742..cbca1564f 100644 --- a/test/Tests/Readers/RST.hs +++ b/test/Tests/Readers/RST.hs @@ -2,25 +2,27 @@ {-# LANGUAGE ScopedTypeVariables #-} module Tests.Readers.RST (tests) where +import Data.Text (Text) +import qualified Data.Text as T import Test.Tasty import Tests.Helpers import Text.Pandoc import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder -rst :: String -> Pandoc +rst :: Text -> Pandoc rst = purely $ readRST def{ readerStandalone = True } infix 4 =: (=:) :: ToString c - => String -> (String, c) -> TestTree + => String -> (Text, c) -> TestTree (=:) = test rst tests :: [TestTree] tests = [ "line block with blank line" =: "| a\n|\n| b" =?> lineBlock [ "a", mempty, "\160b" ] , testGroup "field list" - [ "general" =: unlines + [ "general" =: T.unlines [ "para" , "" , ":Hostname: media08" @@ -44,7 +46,7 @@ tests = [ "line block with blank line" =: , (text "Parameter i", [para "integer"]) , (str "Final", [para "item\non two lines"]) ]) - , "metadata" =: unlines + , "metadata" =: T.unlines [ "=====" , "Title" , "=====" @@ -58,7 +60,7 @@ tests = [ "line block with blank line" =: $ setMeta "title" ("Title" :: Inlines) $ setMeta "subtitle" ("Subtitle" :: Inlines) $ doc mempty ) - , "with inline markup" =: unlines + , "with inline markup" =: T.unlines [ ":*Date*: today" , "" , ".." @@ -80,7 +82,7 @@ tests = [ "line block with blank line" =: ]) ] , "URLs with following punctuation" =: - ("http://google.com, http://yahoo.com; http://foo.bar.baz.\n" ++ + ("http://google.com, http://yahoo.com; http://foo.bar.baz.\n" <> "http://foo.bar/baz_(bam) (http://foo.bar)") =?> para (link "http://google.com" "" "http://google.com" <> ", " <> link "http://yahoo.com" "" "http://yahoo.com" <> "; " <> @@ -89,10 +91,10 @@ tests = [ "line block with blank line" =: link "http://foo.bar/baz_(bam)" "" "http://foo.bar/baz_(bam)" <> " (" <> link "http://foo.bar" "" "http://foo.bar" <> ")") , "Reference names with special characters" =: - ("A-1-B_2_C:3:D+4+E.5.F_\n\n" ++ + ("A-1-B_2_C:3:D+4+E.5.F_\n\n" <> ".. _A-1-B_2_C:3:D+4+E.5.F: https://example.com\n") =?> para (link "https://example.com" "" "A-1-B_2_C:3:D+4+E.5.F") - , "Code directive with class and number-lines" =: unlines + , "Code directive with class and number-lines" =: T.unlines [ ".. code::python" , " :number-lines: 34" , " :class: class1 class2 class3" @@ -107,7 +109,7 @@ tests = [ "line block with blank line" =: ) "def func(x):\n return y" ) - , "Code directive with number-lines, no line specified" =: unlines + , "Code directive with number-lines, no line specified" =: T.unlines [ ".. code::python" , " :number-lines: " , "" @@ -122,7 +124,7 @@ tests = [ "line block with blank line" =: "def func(x):\n return y" ) , testGroup "literal / line / code blocks" - [ "indented literal block" =: unlines + [ "indented literal block" =: T.unlines [ "::" , "" , " block quotes" @@ -163,7 +165,7 @@ tests = [ "line block with blank line" =: , "unknown role" =: ":unknown:`text`" =?> para (str "text") ] , testGroup "footnotes" - [ "remove space before note" =: unlines + [ "remove space before note" =: T.unlines [ "foo [1]_" , "" , ".. [1]" diff --git a/test/Tests/Readers/Txt2Tags.hs b/test/Tests/Readers/Txt2Tags.hs index f6fa4f989..580815279 100644 --- a/test/Tests/Readers/Txt2Tags.hs +++ b/test/Tests/Readers/Txt2Tags.hs @@ -2,6 +2,8 @@ module Tests.Readers.Txt2Tags (tests) where import Data.List (intersperse) +import Data.Text (Text) +import qualified Data.Text as T import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -9,7 +11,7 @@ import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder import Text.Pandoc.Class -t2t :: String -> Pandoc +t2t :: Text -> Pandoc -- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def t2t = purely $ \s -> do putCommonState @@ -20,7 +22,7 @@ t2t = purely $ \s -> do infix 4 =: (=:) :: ToString c - => String -> (String, c) -> TestTree + => String -> (Text, c) -> TestTree (=:) = test t2t spcSep :: [Inlines] -> Inlines @@ -154,7 +156,7 @@ tests = "== header ==[lab/el]" =?> para (text "== header ==[lab/el]") , "Headers not preceded by a blank line" =: - unlines [ "++ eat dinner ++" + T.unlines [ "++ eat dinner ++" , "Spaghetti and meatballs tonight." , "== walk dog ==" ] =?> @@ -168,16 +170,16 @@ tests = para "=five" , "Paragraph containing asterisk at beginning of line" =: - unlines [ "lucky" + T.unlines [ "lucky" , "*star" ] =?> para ("lucky" <> softbreak <> "*star") , "Horizontal Rule" =: - unlines [ "before" - , replicate 20 '-' - , replicate 20 '=' - , replicate 20 '_' + T.unlines [ "before" + , T.replicate 20 "-" + , T.replicate 20 "=" + , T.replicate 20 "_" , "after" ] =?> mconcat [ para "before" @@ -188,7 +190,7 @@ tests = ] , "Comment Block" =: - unlines [ "%%%" + T.unlines [ "%%%" , "stuff" , "bla" , "%%%"] =?> @@ -199,14 +201,14 @@ tests = , testGroup "Lists" $ [ "Simple Bullet Lists" =: - ("- Item1\n" ++ + ("- Item1\n" <> "- Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" ] , "Indented Bullet Lists" =: - (" - Item1\n" ++ + (" - Item1\n" <> " - Item2\n") =?> bulletList [ plain "Item1" , plain "Item2" @@ -215,13 +217,13 @@ tests = , "Nested Bullet Lists" =: - ("- Discovery\n" ++ - " + One More Time\n" ++ - " + Harder, Better, Faster, Stronger\n" ++ - "- Homework\n" ++ - " + Around the World\n"++ - "- Human After All\n" ++ - " + Technologic\n" ++ + ("- Discovery\n" <> + " + One More Time\n" <> + " + Harder, Better, Faster, Stronger\n" <> + "- Homework\n" <> + " + Around the World\n"<> + "- Human After All\n" <> + " + Technologic\n" <> " + Robot Rock\n") =?> bulletList [ mconcat [ plain "Discovery" @@ -250,7 +252,7 @@ tests = ] , "Simple Ordered List" =: - ("+ Item1\n" ++ + ("+ Item1\n" <> "+ Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" @@ -260,7 +262,7 @@ tests = , "Indented Ordered List" =: - (" + Item1\n" ++ + (" + Item1\n" <> " + Item2\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ plain "Item1" @@ -269,11 +271,11 @@ tests = in orderedListWith listStyle listStructure , "Nested Ordered Lists" =: - ("+ One\n" ++ - " + One-One\n" ++ - " + One-Two\n" ++ - "+ Two\n" ++ - " + Two-One\n"++ + ("+ One\n" <> + " + One-One\n" <> + " + One-Two\n" <> + "+ Two\n" <> + " + Two-One\n"<> " + Two-Two\n") =?> let listStyle = (1, DefaultStyle, DefaultDelim) listStructure = [ mconcat @@ -292,19 +294,19 @@ tests = in orderedListWith listStyle listStructure , "Ordered List in Bullet List" =: - ("- Emacs\n" ++ + ("- Emacs\n" <> " + Org\n") =?> bulletList [ (plain "Emacs") <> (orderedList [ plain "Org"]) ] , "Bullet List in Ordered List" =: - ("+ GNU\n" ++ + ("+ GNU\n" <> " - Freedom\n") =?> orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] , "Definition List" =: - unlines [ ": PLL" + T.unlines [ ": PLL" , " phase-locked loop" , ": TTL" , " transistor-transistor logic" @@ -318,7 +320,7 @@ tests = , "Loose bullet list" =: - unlines [ "- apple" + T.unlines [ "- apple" , "" , "- orange" , "" @@ -340,7 +342,7 @@ tests = simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] , "Multi line table" =: - unlines [ "| One |" + T.unlines [ "| One |" , "| Two |" , "| Three |" ] =?> @@ -355,7 +357,7 @@ tests = simpleTable' 1 mempty [[mempty]] , "Glider Table" =: - unlines [ "| 1 | 0 | 0 |" + T.unlines [ "| 1 | 0 | 0 |" , "| 0 | 1 | 1 |" , "| 1 | 1 | 0 |" ] =?> @@ -367,7 +369,7 @@ tests = , "Table with Header" =: - unlines [ "|| Species | Status |" + T.unlines [ "|| Species | Status |" , "| cervisiae | domesticated |" , "| paradoxus | wild |" ] =?> @@ -377,7 +379,7 @@ tests = ] , "Table alignment determined by spacing" =: - unlines [ "| Numbers | Text | More |" + T.unlines [ "| Numbers | Text | More |" , "| 1 | One | foo |" , "| 2 | Two | bar |" ] =?> @@ -394,7 +396,7 @@ tests = , "Table with differing row lengths" =: - unlines [ "|| Numbers | Text " + T.unlines [ "|| Numbers | Text " , "| 1 | One | foo |" , "| 2 " ] =?> @@ -408,23 +410,23 @@ tests = , testGroup "Blocks and fragments" [ "Source block" =: - unlines [ "```" + T.unlines [ "```" , "main = putStrLn greeting" , " where greeting = \"moin\"" , "```" ] =?> - let code' = "main = putStrLn greeting\n" ++ + let code' = "main = putStrLn greeting\n" <> " where greeting = \"moin\"\n" in codeBlock code' , "tagged block" =: - unlines [ "'''" + T.unlines [ "'''" , "<aside>HTML5 is pretty nice.</aside>" , "'''" ] =?> rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" , "Quote block" =: - unlines ["\t//Niemand// hat die Absicht, eine Mauer zu errichten!" + T.unlines ["\t//Niemand// hat die Absicht, eine Mauer zu errichten!" ] =?> blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," , "eine", "Mauer", "zu", "errichten!" diff --git a/test/Tests/Writers/AsciiDoc.hs b/test/Tests/Writers/AsciiDoc.hs index 02ecb08f4..6b97c0761 100644 --- a/test/Tests/Writers/AsciiDoc.hs +++ b/test/Tests/Writers/AsciiDoc.hs @@ -1,5 +1,6 @@ module Tests.Writers.AsciiDoc (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -7,7 +8,7 @@ import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder asciidoc :: (ToPandoc a) => a -> String -asciidoc = purely (writeAsciiDoc def{ writerWrapText = WrapNone }) . toPandoc +asciidoc = unpack . purely (writeAsciiDoc def{ writerWrapText = WrapNone }) . toPandoc tests :: [TestTree] tests = [ testGroup "emphasis" diff --git a/test/Tests/Writers/ConTeXt.hs b/test/Tests/Writers/ConTeXt.hs index a5185e19f..783b601a9 100644 --- a/test/Tests/Writers/ConTeXt.hs +++ b/test/Tests/Writers/ConTeXt.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.ConTeXt (tests) where +import Data.Text (unpack) import Test.Tasty import Test.Tasty.QuickCheck import Tests.Helpers @@ -9,10 +10,10 @@ import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder context :: (ToPandoc a) => a -> String -context = purely (writeConTeXt def) . toPandoc +context = unpack . purely (writeConTeXt def) . toPandoc context' :: (ToPandoc a) => a -> String -context' = purely (writeConTeXt def{ writerWrapText = WrapNone }) . toPandoc +context' = unpack . purely (writeConTeXt def{ writerWrapText = WrapNone }) . toPandoc {- "my test" =: X =?> Y diff --git a/test/Tests/Writers/Docbook.hs b/test/Tests/Writers/Docbook.hs index d7da51aed..90ae073fa 100644 --- a/test/Tests/Writers/Docbook.hs +++ b/test/Tests/Writers/Docbook.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.Docbook (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -11,7 +12,7 @@ docbook :: (ToPandoc a) => a -> String docbook = docbookWithOpts def{ writerWrapText = WrapNone } docbookWithOpts :: ToPandoc a => WriterOptions -> a -> String -docbookWithOpts opts = purely (writeDocbook4 opts) . toPandoc +docbookWithOpts opts = unpack . purely (writeDocbook4 opts) . toPandoc {- "my test" =: X =?> Y diff --git a/test/Tests/Writers/Docx.hs b/test/Tests/Writers/Docx.hs index 2d7179199..215952893 100644 --- a/test/Tests/Writers/Docx.hs +++ b/test/Tests/Writers/Docx.hs @@ -10,6 +10,8 @@ import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.Native import Text.Pandoc.Writers.Docx import System.IO.Unsafe (unsafePerformIO) -- TODO temporary +import qualified Data.ByteString as BS +import qualified Text.Pandoc.UTF8 as UTF8 type Options = (WriterOptions, ReaderOptions) @@ -18,8 +20,8 @@ compareOutput :: Options -> FilePath -> IO (Pandoc, Pandoc) compareOutput opts nativeFileIn nativeFileOut = do - nf <- Prelude.readFile nativeFileIn - nf' <- Prelude.readFile nativeFileOut + nf <- UTF8.toText <$> BS.readFile nativeFileIn + nf' <- UTF8.toText <$> BS.readFile nativeFileOut let wopts = fst opts df <- runIOorExplode $ do d <- readNative def nf diff --git a/test/Tests/Writers/HTML.hs b/test/Tests/Writers/HTML.hs index 4246b033d..23ff718d3 100644 --- a/test/Tests/Writers/HTML.hs +++ b/test/Tests/Writers/HTML.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.HTML (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -8,7 +9,7 @@ import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder html :: (ToPandoc a) => a -> String -html = purely (writeHtml4String def{ writerWrapText = WrapNone }) . toPandoc +html = unpack . purely (writeHtml4String def{ writerWrapText = WrapNone }) . toPandoc {- "my test" =: X =?> Y diff --git a/test/Tests/Writers/LaTeX.hs b/test/Tests/Writers/LaTeX.hs index 5f8aea3e0..471d9d9e7 100644 --- a/test/Tests/Writers/LaTeX.hs +++ b/test/Tests/Writers/LaTeX.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Writers.LaTeX (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -14,10 +15,10 @@ latexListing :: (ToPandoc a) => a -> String latexListing = latexWithOpts def{ writerListings = True } latexWithOpts :: (ToPandoc a) => WriterOptions -> a -> String -latexWithOpts opts = purely (writeLaTeX opts) . toPandoc +latexWithOpts opts = unpack . purely (writeLaTeX opts) . toPandoc beamerWithOpts :: (ToPandoc a) => WriterOptions -> a -> String -beamerWithOpts opts = purely (writeBeamer opts) . toPandoc +beamerWithOpts opts = unpack . purely (writeBeamer opts) . toPandoc {- "my test" =: X =?> Y diff --git a/test/Tests/Writers/Markdown.hs b/test/Tests/Writers/Markdown.hs index 5b1e76a29..012e0888c 100644 --- a/test/Tests/Writers/Markdown.hs +++ b/test/Tests/Writers/Markdown.hs @@ -2,6 +2,7 @@ {-# OPTIONS_GHC -fno-warn-name-shadowing #-} module Tests.Writers.Markdown (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -12,10 +13,10 @@ defopts :: WriterOptions defopts = def{ writerExtensions = pandocExtensions } markdown :: (ToPandoc a) => a -> String -markdown = purely (writeMarkdown defopts) . toPandoc +markdown = unpack . purely (writeMarkdown defopts) . toPandoc markdownWithOpts :: (ToPandoc a) => WriterOptions -> a -> String -markdownWithOpts opts x = purely (writeMarkdown opts) $ toPandoc x +markdownWithOpts opts x = unpack . purely (writeMarkdown opts) $ toPandoc x {- "my test" =: X =?> Y diff --git a/test/Tests/Writers/Muse.hs b/test/Tests/Writers/Muse.hs index 9a7dec580..63fdd293c 100644 --- a/test/Tests/Writers/Muse.hs +++ b/test/Tests/Writers/Muse.hs @@ -1,5 +1,6 @@ module Tests.Writers.Muse (tests) where +import Data.Text (unpack) import Test.Tasty import Tests.Helpers import Text.Pandoc @@ -10,7 +11,7 @@ muse :: (ToPandoc a) => a -> String muse = museWithOpts def{ writerWrapText = WrapNone } museWithOpts :: (ToPandoc a) => WriterOptions -> a -> String -museWithOpts opts = purely (writeMuse opts) . toPandoc +museWithOpts opts = unpack . purely (writeMuse opts) . toPandoc infix 4 =: (=:) :: (ToString a, ToPandoc a) @@ -155,8 +156,8 @@ tests = [ testGroup "block elements" ,[para $ text "Para 2.1", para $ text "Para 2.2"]] in simpleTable [] rows =?> - unlines [ "Para 1.1 | Para 1.2" - , "Para 2.1 | Para 2.2" + unlines [ " Para 1.1 | Para 1.2" + , " Para 2.1 | Para 2.2" ] , "table with header" =: let headers = [plain $ text "header 1", plain $ text "header 2"] @@ -164,9 +165,9 @@ tests = [ testGroup "block elements" ,[para $ text "Para 2.1", para $ text "Para 2.2"]] in simpleTable headers rows =?> - unlines [ "header 1 || header 2" - , "Para 1.1 | Para 1.2" - , "Para 2.1 | Para 2.2" + unlines [ " header 1 || header 2" + , " Para 1.1 | Para 1.2" + , " Para 2.1 | Para 2.2" ] , "table with header and caption" =: let caption = text "Table 1" @@ -174,10 +175,10 @@ tests = [ testGroup "block elements" rows = [[para $ text "Para 1.1", para $ text "Para 1.2"] ,[para $ text "Para 2.1", para $ text "Para 2.2"]] in table caption mempty headers rows - =?> unlines [ "header 1 || header 2" - , "Para 1.1 | Para 1.2" - , "Para 2.1 | Para 2.2" - , "|+ Table 1 +|" + =?> unlines [ " header 1 || header 2" + , " Para 1.1 | Para 1.2" + , " Para 2.1 | Para 2.2" + , " |+ Table 1 +|" ] ] -- Div is trivial diff --git a/test/Tests/Writers/Native.hs b/test/Tests/Writers/Native.hs index c92cb905c..c22185968 100644 --- a/test/Tests/Writers/Native.hs +++ b/test/Tests/Writers/Native.hs @@ -1,5 +1,6 @@ module Tests.Writers.Native (tests) where +import Data.Text (unpack) import Test.Tasty import Test.Tasty.QuickCheck import Tests.Helpers @@ -8,12 +9,11 @@ import Text.Pandoc.Arbitrary () p_write_rt :: Pandoc -> Bool p_write_rt d = - read (purely (writeNative def{ writerTemplate = Just "" }) d) == d + read (unpack $ purely (writeNative def{ writerTemplate = Just "" }) d) == d p_write_blocks_rt :: [Block] -> Bool p_write_blocks_rt bs = - read (purely (writeNative def) (Pandoc nullMeta bs)) == - bs + read (unpack $ purely (writeNative def) (Pandoc nullMeta bs)) == bs tests :: [TestTree] tests = [ testProperty "p_write_rt" p_write_rt diff --git a/test/command/1718.md b/test/command/1718.md new file mode 100644 index 000000000..401610a7a --- /dev/null +++ b/test/command/1718.md @@ -0,0 +1,11 @@ +``` +% pandoc -t native +Note[^1]. + +[^1]: the first note. + +[^2]: the second, unused, note. +^D +[warning] Note with key '2' defined at line 5 column 1 but not used. +[Para [Str "Note",Note [Para [Str "the",Space,Str "first",Space,Str "note."]],Str "."]] +``` diff --git a/test/command/1841.md b/test/command/1841.md new file mode 100644 index 000000000..408f224bd --- /dev/null +++ b/test/command/1841.md @@ -0,0 +1,42 @@ +``` +% pandoc +<table> +<tr> +<td> *one*</td> +<td> [a link](http://google.com)</td> +</tr> +</table> +^D +<table> +<tr> +<td> +<em>one</em> +</td> +<td> +<a href="http://google.com">a link</a> +</td> +</tr> +</table> +``` + +``` +% pandoc +<table> + <tr> + <td>*one*</td> + <td>[a link](http://google.com)</td> + </tr> +</table> +^D +<table> +<tr> +<td> +<em>one</em> +</td> +<td> +<a href="http://google.com">a link</a> +</td> +</tr> +</table> +``` + diff --git a/test/command/2228.md b/test/command/2228.md new file mode 100644 index 000000000..589a2350e --- /dev/null +++ b/test/command/2228.md @@ -0,0 +1,6 @@ +``` +% pandoc -f markdown+smart -t latex+smart +*foo*'s 'foo' +^D +\emph{foo}'s `foo' +``` diff --git a/test/command/2602.md b/test/command/2602.md new file mode 100644 index 000000000..5ed4b581c --- /dev/null +++ b/test/command/2602.md @@ -0,0 +1,18 @@ +``` +% pandoc +[a] [b] + +[b]: url +^D +<p>[a] <a href="url">b</a></p> +``` + +``` +% pandoc -f markdown+spaced_reference_links +[a] [b] + +[b]: url +^D +<p><a href="url">a</a></p> +``` + diff --git a/test/command/3113.md b/test/command/3113.md new file mode 100644 index 000000000..f44e25709 --- /dev/null +++ b/test/command/3113.md @@ -0,0 +1,13 @@ +``` +% pandoc -f latex -t native +\begin{eqnarray} +A&=&B,\\ +C&=&D,\\ +%\end{eqnarray} +%\begin{eqnarray} +E&=&F +\end{eqnarray} +^D +[Para [Math DisplayMath "\\begin{aligned}\nA&=&B,\\\\\nC&=&D,\\\\\nE&=&F\\end{aligned}"]] +``` + diff --git a/test/command/3314.md b/test/command/3314.md new file mode 100644 index 000000000..064b04cbd --- /dev/null +++ b/test/command/3314.md @@ -0,0 +1,34 @@ +See #3315 and <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#simple-tables>. + +``` +% pandoc -f org -t html5 ++-----------+-------+----------+ +| First | 12.0 | Example | +| | | row | +| | | spanning | +| | | lines | ++-----------+-------+----------+ +| Second | 5.0 | Another | ++-----------+-------+----------+ +^D +<table style="width:43%;"> +<colgroup> +<col style="width: 16%" /> +<col style="width: 11%" /> +<col style="width: 15%" /> +</colgroup> +<tbody> +<tr class="odd"> +<td>First</td> +<td>12.0</td> +<td>Example row spanning lines</td> +</tr> +<tr class="even"> +<td>Second</td> +<td>5.0</td> +<td>Another</td> +</tr> +</tbody> +</table> +``` + diff --git a/test/command/3401.md b/test/command/3401.md new file mode 100644 index 000000000..99528553a --- /dev/null +++ b/test/command/3401.md @@ -0,0 +1,19 @@ +See #3401 and <http://orgmode.org/manual/Macro-replacement.html> + +``` +% pandoc -f org -t native +#+MACRO: HELLO /Hello, $1/ +{{{HELLO(World)}}} +^D +[Para [Emph [Str "Hello,",Space,Str "World"]]] +``` + +Inverted argument order + +``` +% pandoc -f org -t native +#+MACRO: A $2,$1 +{{{A(1,2)}}} +^D +[Para [Str "2,1"]] +``` diff --git a/test/command/3432.md b/test/command/3432.md new file mode 100644 index 000000000..7264d22c3 --- /dev/null +++ b/test/command/3432.md @@ -0,0 +1,289 @@ +List-table with header-rows and widths options. + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + :widths: 15 10 30 + :header-rows: 1 + + * - Treat + - Quantity + - Description + * - Albatross + - 2.99 + - On a stick! + * - Crunchy Frog + - 1.49 + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<colgroup> +<col style="width: 27%" /> +<col style="width: 18%" /> +<col style="width: 54%" /> +</colgroup> +<thead> +<tr class="header"> +<th>Treat</th> +<th>Quantity</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td>On a stick!</td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td>1.49</td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` + +List-table whose widths is "auto". + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + :header-rows: 1 + :widths: auto + + * - Treat + - Quantity + - Description + * - Albatross + - 2.99 + - On a stick! + * - Crunchy Frog + - 1.49 + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<thead> +<tr class="header"> +<th>Treat</th> +<th>Quantity</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td>On a stick!</td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td>1.49</td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` + + +List-table with header-rows which is bigger than 1. Only the first row is treated as a header. + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + :header-rows: 2 + + * - Treat + - Quantity + - Description + * - Albatross + - 2.99 + - On a stick! + * - Crunchy Frog + - 1.49 + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<thead> +<tr class="header"> +<th>Treat</th> +<th>Quantity</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td>On a stick!</td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td>1.49</td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` + +List-table without header-rows. + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + + * - Albatross + - 2.99 + - On a stick! + * - Crunchy Frog + - 1.49 + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td>On a stick!</td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td>1.49</td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` + +List-table with empty cells. You need a space after '-', otherwise the row will disapear. Parser for Bulletlists causes this ristriction. + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + :header-rows: 2 + + * - Treat + - Quantity + - Description + * - Albatross + - 2.99 + - + * - Crunchy Frog + - + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<thead> +<tr class="header"> +<th>Treat</th> +<th>Quantity</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td></td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td></td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` + +List-table with a cell having a bulletlist + +``` +% pandoc -f rst +.. list-table:: Frozen Delights! + + * - Albatross + - 2.99 + - + On a stick! + + In a cup! + * - Crunchy Frog + - 1.49 + - If we took the bones out, it wouldn't be + crunchy, now would it? + * - Gannet Ripple + - 1.99 + - On a stick! +^D +<table> +<caption>Frozen Delights!</caption> +<tbody> +<tr class="odd"> +<td>Albatross</td> +<td>2.99</td> +<td><ul> +<li>On a stick!</li> +<li>In a cup!</li> +</ul></td> +</tr> +<tr class="even"> +<td>Crunchy Frog</td> +<td>1.49</td> +<td>If we took the bones out, it wouldn't be crunchy, now would it?</td> +</tr> +<tr class="odd"> +<td>Gannet Ripple</td> +<td>1.99</td> +<td>On a stick!</td> +</tr> +</tbody> +</table> +``` diff --git a/test/command/3450.md b/test/command/3450.md new file mode 100644 index 000000000..8759aa0c1 --- /dev/null +++ b/test/command/3450.md @@ -0,0 +1,12 @@ +``` +% pandoc -fmarkdown-implicit_figures +![image](lalune.jpg){height=2em} +^D +<p><img src="lalune.jpg" alt="image" style="height:2em" /></p> +``` +``` +% pandoc -fmarkdown-implicit_figures -t latex +![image](lalune.jpg){height=2em} +^D +\includegraphics[height=2em]{lalune.jpg} +``` diff --git a/test/command/3494.md b/test/command/3494.md index faa58c321..7c480fde6 100644 --- a/test/command/3494.md +++ b/test/command/3494.md @@ -1,5 +1,5 @@ ``` -% pandoc -f latex +% pandoc -f latex --quiet \begin{table}[h!] \begin{tabular}{r|l|l} diff --git a/test/command/3510-export.latex b/test/command/3510-export.latex new file mode 100644 index 000000000..6d8636322 --- /dev/null +++ b/test/command/3510-export.latex @@ -0,0 +1 @@ +\emph{Hello}
\ No newline at end of file diff --git a/test/command/3510-src.hs b/test/command/3510-src.hs new file mode 100644 index 000000000..ad5744b80 --- /dev/null +++ b/test/command/3510-src.hs @@ -0,0 +1 @@ +putStrLn outString diff --git a/test/command/3510-subdoc.org b/test/command/3510-subdoc.org new file mode 100644 index 000000000..5bcc6678a --- /dev/null +++ b/test/command/3510-subdoc.org @@ -0,0 +1,5 @@ +* Subsection + +Included text + +Lorem ipsum. diff --git a/test/command/3510.md b/test/command/3510.md new file mode 100644 index 000000000..7993db848 --- /dev/null +++ b/test/command/3510.md @@ -0,0 +1,20 @@ +See <http://orgmode.org/manual/Include-files.html> +``` +% pandoc -f org -t native +Text + +#+include: "command/3510-subdoc.org" + +#+INCLUDE: "command/3510-src.hs" src haskell +#+INCLUDE: "command/3510-export.latex" export latex + +More text +^D +[Para [Str "Text"] +,Header 1 ("subsection",[],[]) [Str "Subsection"] +,Para [Str "Included",Space,Str "text"] +,Plain [Str "Lorem",Space,Str "ipsum."] +,CodeBlock ("",["haskell"],[]) "putStrLn outString\n" +,RawBlock (Format "latex") "\\emph{Hello}" +,Para [Str "More",Space,Str "text"]] +``` diff --git a/test/command/3516.md b/test/command/3516.md index 982043874..8c7e478d3 100644 --- a/test/command/3516.md +++ b/test/command/3516.md @@ -27,8 +27,8 @@ on Windows builds. [Table [] [AlignDefault,AlignDefault] [5.555555555555555e-2,5.555555555555555e-2] [[] ,[]] - [[[Para [Str "1"]] - ,[Para [Str "2"]]] + [[[Plain [Str "1"]] + ,[Plain [Str "2"]]] ,[[] ,[]]]] ``` diff --git a/test/command/3577.md b/test/command/3577.md index bfeb86eaa..dc88937e9 100644 --- a/test/command/3577.md +++ b/test/command/3577.md @@ -1,5 +1,5 @@ ``` -% pandoc -f latex -t html5 +% pandoc -f latex -t html5 --quiet \begin{figure}[ht] \begin{subfigure}{0.45\textwidth} \centering diff --git a/test/command/3585.md b/test/command/3585.md new file mode 100644 index 000000000..739ddeea4 --- /dev/null +++ b/test/command/3585.md @@ -0,0 +1,16 @@ +``` +% pandoc -f mediawiki+smart -t native +"Hello" + +Same but bzip2 it and nice it <tt>zfs send tank/storage/data/svn@daily-2014-03-20_00.00.00--2w | nice -15 bzip2 | ssh user@hyper.somewhere.org "> /storage/c-3po/tank-storage-data-svn.dmp.bz2"</tt> +^D +[Para [Quoted DoubleQuote [Str "Hello"]] +,Para [Str "Same",Space,Str "but",Space,Str "bzip2",Space,Str "it",Space,Str "and",Space,Str "nice",Space,Str "it",Space,Code ("",[],[]) "zfs send tank/storage/data/svn@daily-2014-03-20_00.00.00--2w | nice -15 bzip2 | ssh user@hyper.somewhere.org \"> /storage/c-3po/tank-storage-data-svn.dmp.bz2\""]] +``` + +``` +% pandoc -f mediawiki -t native +"Hello" +^D +[Para [Str "\"Hello\""]] +``` diff --git a/test/command/3619.md b/test/command/3619.md new file mode 100644 index 000000000..62962c43b --- /dev/null +++ b/test/command/3619.md @@ -0,0 +1,28 @@ +``` +% pandoc -f html -t markdown --reference-links +<a href="foo">bar</a>: baz +^D +[bar][]: baz + + [bar]: foo +``` + +``` +% pandoc -f html -t markdown --reference-links +<a href="foo">bar</a>(baz) +^D +[bar][](baz) + + [bar]: foo +``` + +``` +% pandoc -f html -t markdown_strict --reference-links +<a href="a">foo</a><br/><a href="b">bar</a> +^D +[foo][] +[bar] + + [foo]: a + [bar]: b +``` diff --git a/test/command/3630.md b/test/command/3630.md new file mode 100644 index 000000000..db3a17dda --- /dev/null +++ b/test/command/3630.md @@ -0,0 +1,8 @@ +``` +% pandoc -f markdown -t markdown --reference-links +![foo](bar.png){#myId} +^D +![foo] + + [foo]: bar.png {#myId} +``` diff --git a/test/command/3667.md b/test/command/3667.md new file mode 100644 index 000000000..97de8f598 --- /dev/null +++ b/test/command/3667.md @@ -0,0 +1,13 @@ +``` +% pandoc -f textile +| "link text":http://example.com/ | +^D +<table> +<tbody> +<tr class="odd"> +<td><a href="http://example.com/">link text</a></td> +</tr> +</tbody> +</table> +``` + diff --git a/test/command/3674.md b/test/command/3674.md new file mode 100644 index 000000000..92ed4bed7 --- /dev/null +++ b/test/command/3674.md @@ -0,0 +1,39 @@ +Make sure we don't get duplicate reference links, even with +`--reference-location=section`. + +``` +% pandoc --reference-links -t markdown --reference-location=section --atx-headers +# a + +![](a) + +# b + +![](b) + +^D +# a + +![][1] + + [1]: a + +# b + +![][2] + + [2]: b +``` + +Subsidiary issue: allow line break between reference link +url/title and attributes: + +``` +% pandoc +[a] + +[a]: url +{.class} +^D +<p><a href="url" class="class">a</a></p> +``` diff --git a/test/command/3675.md b/test/command/3675.md new file mode 100644 index 000000000..b129c7a63 --- /dev/null +++ b/test/command/3675.md @@ -0,0 +1,15 @@ +```` +% pandoc -t rst +```python +print("hello") +``` +> block quote +^D +.. code:: python + + print("hello") + +.. + + block quote +```` diff --git a/test/command/3690.md b/test/command/3690.md new file mode 100644 index 000000000..213b88138 --- /dev/null +++ b/test/command/3690.md @@ -0,0 +1,8 @@ +``` +% pandoc +- [o] _hi_ +^D +<ul> +<li>[o] <em>hi</em></li> +</ul> +``` diff --git a/test/command/3701.md b/test/command/3701.md new file mode 100644 index 000000000..01e438639 --- /dev/null +++ b/test/command/3701.md @@ -0,0 +1,60 @@ +``` +% pandoc --reference-location=block -t markdown --reference-links --wrap=preserve +[a](u) + +[a](u) + +[a](u2) +[A](u) +[a](u){.foo} + +[a](u3) +^D +[a] + + [a]: u + +[a] + + [a]: u + +[a][1] +[A][] +[a][2] + + [1]: u2 + [A]: u + [2]: u {.foo} + +[a][3] + + [3]: u3 +``` + +``` +% pandoc +[a] + + [a]: u + +[a] + + [a]: u + +[a][1] +[A][] +[a][2] + + [1]: u2 + [A]: u + [2]: u {.foo} + +[a][3] + + [3]: u3 +^D +<p><a href="u">a</a></p> +<p><a href="u">a</a></p> +<p><a href="u2">a</a> <a href="u">A</a> <a href="u" class="foo">a</a></p> +<p><a href="u3">a</a></p> +``` diff --git a/test/command/3706.md b/test/command/3706.md new file mode 100644 index 000000000..00f53279e --- /dev/null +++ b/test/command/3706.md @@ -0,0 +1,44 @@ +Results marker can be hidden in block attributes (#3706) + +``` +pandoc -f org -t native +#+BEGIN_SRC R :exports results :colnames yes + data.frame(Id = 1:3, Desc = rep("La",3)) +#+END_SRC + +#+CAPTION: Lalelu. +#+LABEL: tab +#+RESULTS: +| Id | Desc | +|----+------| +| 1 | La | +| 2 | La | +| 3 | La | +^D +[Table [Str "Lalelu."] [AlignDefault,AlignDefault] [0.0,0.0] + [[Plain [Str "Id"]] + ,[Plain [Str "Desc"]]] + [[[Plain [Str "1"]] + ,[Plain [Str "La"]]] + ,[[Plain [Str "2"]] + ,[Plain [Str "La"]]] + ,[[Plain [Str "3"]] + ,[Plain [Str "La"]]]]] +``` + +``` +pandoc -f org -t native +#+BEGIN_SRC R :exports none :colnames yes + data.frame(Id = 1:2, Desc = rep("La",2)) +#+END_SRC + +#+CAPTION: Lalelu. +#+LABEL: tab +#+RESULTS: +| Id | Desc | +|----+------| +| 1 | La | +| 2 | La | +^D +[] +``` diff --git a/test/command/3708.md b/test/command/3708.md new file mode 100644 index 000000000..2cbc82c25 --- /dev/null +++ b/test/command/3708.md @@ -0,0 +1,15 @@ +``` +% pandoc -f latex -t native +\begin{tabular}{cc} + A & B\&1 \\ + C & D +\end{tabular} +^D +[Table [] [AlignCenter,AlignCenter] [0.0,0.0] + [[] + ,[]] + [[[Plain [Str "A"]] + ,[Plain [Str "B&1"]]] + ,[[Plain [Str "C"]] + ,[Plain [Str "D"]]]]] +``` diff --git a/test/command/3715.md b/test/command/3715.md new file mode 100644 index 000000000..9d74779cb --- /dev/null +++ b/test/command/3715.md @@ -0,0 +1,15 @@ +``` +% pandoc -t markdown -f html --wrap=preserve +x<em></em>x +y<strong></strong>y +z<sup></sup>z +w<sub></sub>w +q<s></s>q +^D +xx +yy +zz +ww +qq +``` + diff --git a/test/command/3716.md b/test/command/3716.md new file mode 100644 index 000000000..7e00819da --- /dev/null +++ b/test/command/3716.md @@ -0,0 +1,6 @@ +``` +% pandoc +<http://example.com>{.foo} +^D +<p><a href="http://example.com" class="uri foo">http://example.com</a></p> +``` diff --git a/test/command/3730.md b/test/command/3730.md new file mode 100644 index 000000000..fbc06cbce --- /dev/null +++ b/test/command/3730.md @@ -0,0 +1,21 @@ +```` +% pandoc +nice line\ +``` +code +``` +^D +<p>nice line<br /> +</p> +<pre><code>code</code></pre> +```` + +``` +% pandoc +# hi\ +there +^D +<h1 id="hi">hi<br /> +</h1> +<p>there</p> +``` diff --git a/test/command/3736.md b/test/command/3736.md new file mode 100644 index 000000000..b66e0a359 --- /dev/null +++ b/test/command/3736.md @@ -0,0 +1,25 @@ +``` +% pandoc --wrap=preserve -f html -t markdown +<h2>hi +there</h2> +^D +hi there +-------- +``` + +``` +% pandoc --wrap=preserve -f html -t markdown +<h2>hi <em>there +again</em></h2> +^D +hi *there again* +---------------- +``` + +``` +% pandoc --wrap=preserve -f html -t markdown +<h2>hi<br>there</h2> +^D +hi there +-------- +``` diff --git a/test/command/512.md b/test/command/512.md index a13c434f6..52e5dbe07 100644 --- a/test/command/512.md +++ b/test/command/512.md @@ -36,6 +36,7 @@ Loop detection: __ link1_ ^D +[warning] Circular reference 'link1' at line 1 column 15 <p><a href="">click here</a></p> ``` diff --git a/test/command/SVG_logo-without-xml-declaration.svg b/test/command/SVG_logo-without-xml-declaration.svg new file mode 100644 index 000000000..febcab6ca --- /dev/null +++ b/test/command/SVG_logo-without-xml-declaration.svg @@ -0,0 +1,32 @@ +<svg viewBox="-50 -50 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>SVG Logo</title> +<rect id="background" x="-50" y="-50" width="100" height="100" rx="4" fill="#f90"/> +<rect id="top-left" x="-50" y="-50" width="50" height="50" rx="4" fill="#ffb13b"/> +<rect id="bottom-right" width="50" height="50" rx="4" fill="#de8500"/> +<use stroke="#f90" stroke-width="22.6" xlink:href="#a"/> +<circle r="26"/> +<use stroke="#000" stroke-width="12" xlink:href="#a"/> +<g id="a"> + <g id="b"> + <g id="c"> + <circle id="n" cy="-31.6" r="7.1" fill="#fff"/> + <path d="m0 31.6v-63.2" stroke="#fff" stroke-width="10"/> + <use y="63.2" xlink:href="#n"/> + </g> + <use transform="rotate(90)" xlink:href="#c"/> + </g> + <use transform="rotate(45)" xlink:href="#b"/> +</g> +<path id="text-backdrop" d="m44.68 0v40c0 3.333-1.667 5-5 5h-79.38c-3.333 0-5-1.667-5-5v-40"/> +<path id="shine" d="m36 4.21c2.9 0 5.3 2.4 5.3 5.3v18c-27.6-3.4-54.9-8-82-7.7v-10.2c0-2.93 2.4-5.3 5.3-5.3z" fill="#3f3f3f"/> +<use stroke="#000" stroke-width="7.4" xlink:href="#s"/> +<g id="svg-text" stroke="#fff" stroke-width="6.4"> + <g id="s"> + <path fill="none" d="m-31.74 31.17a8.26 8.26 0 1 0 8.26 -8.26 8.26 8.26 0 1 1 8.26 -8.26M23.23 23h8.288v 8.26a8.26 8.26 0 0 1 -16.52 0v-16.52a8.26 8.26 0 0 1 16.52 0"/> + <g stroke-width=".5" stroke="#000"> + <path d="m4.76 3h6.83l-8.24 39.8h-6.85l-8.26-39.8h6.85l4.84 23.3z" fill="#fff"/> + <path d="m23.23 19.55v6.9m4.838-11.71h6.9m-70.16 16.43h6.9m9.62-16.52h6.9" stroke-linecap="square"/> + </g> + </g> +</g> +</svg> diff --git a/test/command/SVG_logo.svg b/test/command/SVG_logo.svg new file mode 100644 index 000000000..5333a5ddb --- /dev/null +++ b/test/command/SVG_logo.svg @@ -0,0 +1,33 @@ +<?xml version="1.0"?> +<svg viewBox="-50 -50 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<title>SVG Logo</title> +<rect id="background" x="-50" y="-50" width="100" height="100" rx="4" fill="#f90"/> +<rect id="top-left" x="-50" y="-50" width="50" height="50" rx="4" fill="#ffb13b"/> +<rect id="bottom-right" width="50" height="50" rx="4" fill="#de8500"/> +<use stroke="#f90" stroke-width="22.6" xlink:href="#a"/> +<circle r="26"/> +<use stroke="#000" stroke-width="12" xlink:href="#a"/> +<g id="a"> + <g id="b"> + <g id="c"> + <circle id="n" cy="-31.6" r="7.1" fill="#fff"/> + <path d="m0 31.6v-63.2" stroke="#fff" stroke-width="10"/> + <use y="63.2" xlink:href="#n"/> + </g> + <use transform="rotate(90)" xlink:href="#c"/> + </g> + <use transform="rotate(45)" xlink:href="#b"/> +</g> +<path id="text-backdrop" d="m44.68 0v40c0 3.333-1.667 5-5 5h-79.38c-3.333 0-5-1.667-5-5v-40"/> +<path id="shine" d="m36 4.21c2.9 0 5.3 2.4 5.3 5.3v18c-27.6-3.4-54.9-8-82-7.7v-10.2c0-2.93 2.4-5.3 5.3-5.3z" fill="#3f3f3f"/> +<use stroke="#000" stroke-width="7.4" xlink:href="#s"/> +<g id="svg-text" stroke="#fff" stroke-width="6.4"> + <g id="s">
+ <path fill="none" d="m-31.74 31.17a8.26 8.26 0 1 0 8.26 -8.26 8.26 8.26 0 1 1 8.26 -8.26M23.23 23h8.288v 8.26a8.26 8.26 0 0 1 -16.52 0v-16.52a8.26 8.26 0 0 1 16.52 0"/>
+ <g stroke-width=".5" stroke="#000"> + <path d="m4.76 3h6.83l-8.24 39.8h-6.85l-8.26-39.8h6.85l4.84 23.3z" fill="#fff"/>
+ <path d="m23.23 19.55v6.9m4.838-11.71h6.9m-70.16 16.43h6.9m9.62-16.52h6.9" stroke-linecap="square"/>
+ </g> + </g> +</g> +</svg> diff --git a/test/command/corrupt.svg b/test/command/corrupt.svg new file mode 100644 index 000000000..cfaa697f0 --- /dev/null +++ b/test/command/corrupt.svg @@ -0,0 +1,5 @@ +Lorem ipsum dolor sit amet etiam. A pede dolor neque pretium luctus pharetra vel rutrum. Orci nonummy ac. At eu est tempor +proin wisi. Nunc tincidunt proin. Suspendisse lorem commodo. Integer diam diam semper commodo dictum et tellus eu ultrices +nec erat pulvinar porttitor nulla nulla mauris orci libero eros elementum et possimus voluptate. Velit morbi et. Luctus diam +in. Lorem tincidunt sem dolor rerum mauris. Dis taciti posuere pellentesque sed rutrum. Lectus donec fusce in dictum pede. +In etiam congue. Aliquam aliquet elit arcu mauris enim. Risus at enim. diff --git a/test/command/inkscape-cube.svg b/test/command/inkscape-cube.svg new file mode 100644 index 000000000..995c3c734 --- /dev/null +++ b/test/command/inkscape-cube.svg @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="38.772217mm" + height="46.163891mm" + viewBox="0 0 38.772217 46.163891" + version="1.1" + id="svg8" + inkscape:version="0.92.1 r" + sodipodi:docname="cube.svg"> + <defs + id="defs2"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-48.380952 : -45.023815 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="161.61905 : -45.023817 : 1" + inkscape:persp3d-origin="56.619048 : -94.523816 : 1" + id="perspective4485" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="-63.181251" + inkscape:cy="-116.38602" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:window-width="1920" + inkscape:window-height="1136" + inkscape:window-x="1920" + inkscape:window-y="30" + inkscape:window-maximized="1" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-149.67857,78.746839)"> + <g + sodipodi:type="inkscape:box3d" + id="g4487" + style="opacity:0.2;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.53100002;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + inkscape:perspectiveID="#perspective4485" + inkscape:corner0="1.1045097 : 0.18860662 : 0 : 1" + inkscape:corner7="0.52634769 : 0.15538942 : 0.25 : 1"> + <path + sodipodi:type="inkscape:box3dside" + id="path4489" + style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="6" + d="m 151.19047,-53.658435 v 15.783818 l 17.00006,5.342459 v -14.107905 z" + points="151.19047,-37.874617 168.19053,-32.532158 168.19053,-46.640063 151.19047,-53.658435 " /> + <path + sodipodi:type="inkscape:box3dside" + id="path4499" + style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="11" + d="m 168.19053,-46.640063 21.77216,-19.229539 v 18.699717 l -21.77216,14.637727 z" + points="189.96269,-65.869602 189.96269,-47.169885 168.19053,-32.532158 168.19053,-46.640063 " /> + <path + sodipodi:type="inkscape:box3dside" + id="path4491" + style="fill:#4d4d9f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="5" + d="m 151.19047,-53.658435 18.89881,-25.037614 19.87341,12.826447 -21.77216,19.229539 z" + points="170.08928,-78.696049 189.96269,-65.869602 168.19053,-46.640063 151.19047,-53.658435 " /> + <path + sodipodi:type="inkscape:box3dside" + id="path4497" + style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="13" + d="m 151.19047,-37.874617 18.89881,-19.058894 19.87341,9.763626 -21.77216,14.637727 z" + points="170.08928,-56.933511 189.96269,-47.169885 168.19053,-32.532158 151.19047,-37.874617 " /> + <path + sodipodi:type="inkscape:box3dside" + id="path4495" + style="fill:#d7d7ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="14" + d="m 170.08928,-78.696049 v 21.762538 l 19.87341,9.763626 v -18.699717 z" + points="170.08928,-56.933511 189.96269,-47.169885 189.96269,-65.869602 170.08928,-78.696049 " /> + <path + sodipodi:type="inkscape:box3dside" + id="path4493" + style="fill:#8686bf;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + inkscape:box3dsidetype="3" + d="m 151.19047,-53.658435 18.89881,-25.037614 v 21.762538 l -18.89881,19.058894 z" + points="170.08928,-78.696049 170.08928,-56.933511 151.19047,-37.874617 151.19047,-53.658435 " /> + </g> + </g> +</svg> diff --git a/test/command/latex-fontawesome.md b/test/command/latex-fontawesome.md new file mode 100644 index 000000000..2a7e91185 --- /dev/null +++ b/test/command/latex-fontawesome.md @@ -0,0 +1,13 @@ +``` +% pandoc -f latex -t native +Check: \faCheck +^D +[Para [Str "Check:",Space,Str "\10003"]] +``` + +``` +% pandoc -f latex -t native +Close: \faClose +^D +[Para [Str "Close:",Space,Str "\10007"]] +``` diff --git a/test/command/lstlisting.md b/test/command/lstlisting.md new file mode 100644 index 000000000..d928cc702 --- /dev/null +++ b/test/command/lstlisting.md @@ -0,0 +1,25 @@ +``` +% pandoc -f latex -t native +\begin{lstlisting}[language=Java, caption={Java Example}, label=lst:Hello-World] +public class World { + public static void main(String[] args) { + System.out.println("Hello World"); + } +} +\end{lstlisting} +^D +[CodeBlock ("lst:Hello-World",["java"],[("language","Java"),("caption","Java Example"),("label","lst:Hello-World")]) "public class World {\n public static void main(String[] args) {\n System.out.println(\"Hello World\");\n }\n}"] +``` + +``` +% pandoc -f latex -t native +\begin{lstlisting}[language=Java, escapechar=|, caption={Java Example}, label=lst:Hello-World] +public class World { + public static void main(String[] args) { + System.out.println("Hello World"); + } +} +\end{lstlisting} +^D +[CodeBlock ("lst:Hello-World",["java"],[("language","Java"),("escapechar","|"),("caption","Java Example"),("label","lst:Hello-World")]) "public class World {\n public static void main(String[] args) {\n System.out.println(\"Hello World\");\n }\n}"] +``` diff --git a/test/command/parse-raw.md b/test/command/parse-raw.md index f4e493c69..6c91c2fa9 100644 --- a/test/command/parse-raw.md +++ b/test/command/parse-raw.md @@ -9,6 +9,7 @@ % pandoc -f latex -t markdown \emph{Hi \foo{there}} ^D +[warning] Skipped '\foo{there}' at line 1 column 21 *Hi* ``` @@ -23,5 +24,7 @@ % pandoc -f html -t markdown <em>Hi <blink>there</blink></em> ^D +[warning] Skipped '<blink>' at input line 1 column 8 +[warning] Skipped '</blink>' at input line 1 column 20 *Hi there* ``` diff --git a/test/command/svg.md b/test/command/svg.md new file mode 100644 index 000000000..b48745f9a --- /dev/null +++ b/test/command/svg.md @@ -0,0 +1,132 @@ +``` +% pandoc -f latex -t icml +\includegraphics{command/corrupt.svg} +^D +[warning] Could not determine image size for 'command/corrupt.svg': could not determine image type +<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> + <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 150 -100"> + <Properties> + <PathGeometry> + <GeometryPathType PathOpen="false"> + <PathPointArray> + <PathPointType Anchor="-150 -100" LeftDirection="-150 -100" RightDirection="-150 -100" /> + <PathPointType Anchor="-150 100" LeftDirection="-150 100" RightDirection="-150 100" /> + <PathPointType Anchor="150 100" LeftDirection="150 100" RightDirection="150 100" /> + <PathPointType Anchor="150 -100" LeftDirection="150 -100" RightDirection="150 -100" /> + </PathPointArray> + </GeometryPathType> + </PathGeometry> + </Properties> + <Image Self="ue6" ItemTransform="1 0 0 1 -150 -100"> + <Properties> + <Profile type="string"> + $ID/Embedded + </Profile> + </Properties> + <Link Self="ueb" LinkResourceURI="file:command/corrupt.svg" /> + </Image> + </Rectangle> + </CharacterStyleRange> +</ParagraphStyleRange> +``` + +``` +% pandoc -f latex -t icml +\includegraphics{command/SVG_logo.svg} +^D +[warning] Could not determine image size for 'command/SVG_logo.svg': could not determine SVG size +<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> + <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 150 -100"> + <Properties> + <PathGeometry> + <GeometryPathType PathOpen="false"> + <PathPointArray> + <PathPointType Anchor="-150 -100" LeftDirection="-150 -100" RightDirection="-150 -100" /> + <PathPointType Anchor="-150 100" LeftDirection="-150 100" RightDirection="-150 100" /> + <PathPointType Anchor="150 100" LeftDirection="150 100" RightDirection="150 100" /> + <PathPointType Anchor="150 -100" LeftDirection="150 -100" RightDirection="150 -100" /> + </PathPointArray> + </GeometryPathType> + </PathGeometry> + </Properties> + <Image Self="ue6" ItemTransform="1 0 0 1 -150 -100"> + <Properties> + <Profile type="string"> + $ID/Embedded + </Profile> + </Properties> + <Link Self="ueb" LinkResourceURI="file:command/SVG_logo.svg" /> + </Image> + </Rectangle> + </CharacterStyleRange> +</ParagraphStyleRange> +``` + +``` +% pandoc -f latex -t icml +\includegraphics{command/SVG_logo-without-xml-declaration.svg} +^D +[warning] Could not determine image size for 'command/SVG_logo-without-xml-declaration.svg': could not determine SVG size +<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> + <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 150 -100"> + <Properties> + <PathGeometry> + <GeometryPathType PathOpen="false"> + <PathPointArray> + <PathPointType Anchor="-150 -100" LeftDirection="-150 -100" RightDirection="-150 -100" /> + <PathPointType Anchor="-150 100" LeftDirection="-150 100" RightDirection="-150 100" /> + <PathPointType Anchor="150 100" LeftDirection="150 100" RightDirection="150 100" /> + <PathPointType Anchor="150 -100" LeftDirection="150 -100" RightDirection="150 -100" /> + </PathPointArray> + </GeometryPathType> + </PathGeometry> + </Properties> + <Image Self="ue6" ItemTransform="1 0 0 1 -150 -100"> + <Properties> + <Profile type="string"> + $ID/Embedded + </Profile> + </Properties> + <Link Self="ueb" LinkResourceURI="file:command/SVG_logo-without-xml-declaration.svg" /> + </Image> + </Rectangle> + </CharacterStyleRange> +</ParagraphStyleRange> +``` + + +``` +% pandoc -f latex -t icml +\includegraphics{command/inkscape-cube.svg} +^D +<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> + <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 54.75 -65.25"> + <Properties> + <PathGeometry> + <GeometryPathType PathOpen="false"> + <PathPointArray> + <PathPointType Anchor="-54.75 -65.25" LeftDirection="-54.75 -65.25" RightDirection="-54.75 -65.25" /> + <PathPointType Anchor="-54.75 65.25" LeftDirection="-54.75 65.25" RightDirection="-54.75 65.25" /> + <PathPointType Anchor="54.75 65.25" LeftDirection="54.75 65.25" RightDirection="54.75 65.25" /> + <PathPointType Anchor="54.75 -65.25" LeftDirection="54.75 -65.25" RightDirection="54.75 -65.25" /> + </PathPointArray> + </GeometryPathType> + </PathGeometry> + </Properties> + <Image Self="ue6" ItemTransform="1 0 0 1 -54.75 -65.25"> + <Properties> + <Profile type="string"> + $ID/Embedded + </Profile> + </Properties> + <Link Self="ueb" LinkResourceURI="file:command/inkscape-cube.svg" /> + </Image> + </Rectangle> + </CharacterStyleRange> +</ParagraphStyleRange> +``` + diff --git a/test/command/tabularx.md b/test/command/tabularx.md new file mode 100644 index 000000000..bf7670e9c --- /dev/null +++ b/test/command/tabularx.md @@ -0,0 +1,110 @@ +``` +% pandoc -f latex -t native --quiet +\begin{tabularx}{\linewidth}{|c|c|c|} +\hline + Column Heading 1 + & Column Heading 2 + & Column Heading 3 \\ +\hline + Cell 1.1 + & Cell 1.2 + & Cell 1.3 \\ +\hline + Cell 2.1 + & Cell 2.2 + & Cell 2.3 \\ +\hline + Cell 3.1 + & Cell 3.2 + & Cell 3.3 \\ +\hline +\end{tabularx} +^D +[Table [] [AlignCenter,AlignCenter,AlignCenter] [0.0,0.0,0.0] + [[Plain [Str "Column",Space,Str "Heading",Space,Str "1"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "2"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "3"]]] + [[[Plain [Str "Cell",Space,Str "1.1"]] + ,[Plain [Str "Cell",Space,Str "1.2"]] + ,[Plain [Str "Cell",Space,Str "1.3"]]] + ,[[Plain [Str "Cell",Space,Str "2.1"]] + ,[Plain [Str "Cell",Space,Str "2.2"]] + ,[Plain [Str "Cell",Space,Str "2.3"]]] + ,[[Plain [Str "Cell",Space,Str "3.1"]] + ,[Plain [Str "Cell",Space,Str "3.2"]] + ,[Plain [Str "Cell",Space,Str "3.3"]]]]] +``` + +``` +% pandoc -f latex -t native --quiet +\begin{tabularx}{\linewidth}{|X|c|p{0.25\linewidth}|} +\hline + Column Heading 1 + & Column Heading 2 + & Column Heading 3 \\ +\hline + Cell 1.1 + & Cell 1.2 + & Cell 1.3 \\ +\hline + Cell 2.1 + & Cell 2.2 + & Cell 2.3 \\ +\hline + Cell 3.1 + & Cell 3.2 + & Cell 3.3 \\ +\hline +\end{tabularx} +^D +[Table [] [AlignLeft,AlignCenter,AlignLeft] [0.0,0.0,0.25] + [[Plain [Str "Column",Space,Str "Heading",Space,Str "1"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "2"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "3"]]] + [[[Plain [Str "Cell",Space,Str "1.1"]] + ,[Plain [Str "Cell",Space,Str "1.2"]] + ,[Plain [Str "Cell",Space,Str "1.3"]]] + ,[[Plain [Str "Cell",Space,Str "2.1"]] + ,[Plain [Str "Cell",Space,Str "2.2"]] + ,[Plain [Str "Cell",Space,Str "2.3"]]] + ,[[Plain [Str "Cell",Space,Str "3.1"]] + ,[Plain [Str "Cell",Space,Str "3.2"]] + ,[Plain [Str "Cell",Space,Str "3.3"]]]]] +``` + +``` +% pandoc -f latex -t native --quiet +\begin{tabularx}{\linewidth}{|b{0.25\linewidth}|c|m{0.25\linewidth}|} +\hline + Column Heading 1 + & Column Heading 2 + & Column Heading 3 \\ +\hline + Cell 1.1 + & Cell 1.2 + & Cell 1.3 \\ +\hline + Cell 2.1 + & Cell 2.2 + & Cell 2.3 \\ +\hline + Cell 3.1 + & Cell 3.2 + & Cell 3.3 \\ +\hline +\end{tabularx} +^D +[Table [] [AlignLeft,AlignCenter,AlignLeft] [0.25,0.0,0.25] + [[Plain [Str "Column",Space,Str "Heading",Space,Str "1"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "2"]] + ,[Plain [Str "Column",Space,Str "Heading",Space,Str "3"]]] + [[[Plain [Str "Cell",Space,Str "1.1"]] + ,[Plain [Str "Cell",Space,Str "1.2"]] + ,[Plain [Str "Cell",Space,Str "1.3"]]] + ,[[Plain [Str "Cell",Space,Str "2.1"]] + ,[Plain [Str "Cell",Space,Str "2.2"]] + ,[Plain [Str "Cell",Space,Str "2.3"]]] + ,[[Plain [Str "Cell",Space,Str "3.1"]] + ,[Plain [Str "Cell",Space,Str "3.2"]] + ,[Plain [Str "Cell",Space,Str "3.3"]]]]] +``` diff --git a/test/fb2/titles.fb2 b/test/fb2/titles.fb2 index 9e8d47e36..0a3b1404e 100644 --- a/test/fb2/titles.fb2 +++ b/test/fb2/titles.fb2 @@ -1,3 +1,3 @@ <?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> +<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></body></FictionBook> diff --git a/test/fb2/titles.markdown b/test/fb2/titles.markdown index cc3d0e0d0..1eaf2ccd5 100644 --- a/test/fb2/titles.markdown +++ b/test/fb2/titles.markdown @@ -4,7 +4,3 @@ This example tests if Pandoc doesn't insert forbidden elements in FictionBook ti # *Emphasized* **Strong** Title -# Title with\ -line break - - diff --git a/test/latex-reader.native b/test/latex-reader.native index f37f1b2ca..d481a714d 100644 --- a/test/latex-reader.native +++ b/test/latex-reader.native @@ -249,10 +249,10 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa ,Para [Str "These",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "superscripts",Space,Str "or",Space,Str "subscripts,",Space,Str "because",Space,Str "of",Space,Str "the",SoftBreak,Str "unescaped",Space,Str "spaces:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a",Math InlineMath "\\sim",Str "b",SoftBreak,Str "c",Math InlineMath "\\sim",Str "d."] ,HorizontalRule ,Header 1 ("smart-quotes-ellipses-dashes",[],[]) [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"] -,Para [Quoted DoubleQuote [Str "Hello,"],Space,Str "said",Space,Str "the",Space,Str "spider.",Space,Quoted DoubleQuote [Quoted SingleQuote [Str "Shelob"],Space,Str "is",Space,Str "my",Space,Str "name."]] +,Para [Quoted DoubleQuote [Str "Hello,"],Space,Str "said",Space,Str "the",Space,Str "spider.",Space,Quoted DoubleQuote [Str "\8198",Quoted SingleQuote [Str "Shelob"],Space,Str "is",Space,Str "my",Space,Str "name."]] ,Para [Quoted SingleQuote [Str "A"],Str ",",Space,Quoted SingleQuote [Str "B"],Str ",",Space,Str "and",Space,Quoted SingleQuote [Str "C"],Space,Str "are",Space,Str "letters."] ,Para [Quoted SingleQuote [Str "Oak,"],Space,Quoted SingleQuote [Str "elm,"],Space,Str "and",Space,Quoted SingleQuote [Str "beech"],Space,Str "are",Space,Str "names",Space,Str "of",Space,Str "trees.",Space,Str "So",Space,Str "is",Space,Quoted SingleQuote [Str "pine."]] -,Para [Quoted SingleQuote [Str "He",Space,Str "said,",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go."]],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70\8217s?"] +,Para [Quoted SingleQuote [Str "He",Space,Str "said,",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go."],Str "\8198"],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70\8217s?"] ,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "quoted",Space,Quoted SingleQuote [Code ("",[],[]) "code"],Space,Str "and",Space,Str "a",SoftBreak,Quoted DoubleQuote [Link ("",[],[]) [Str "quoted",Space,Str "link"] ("http://example.com/?foo=1&bar=2","")],Str "."] ,Para [Str "Some",Space,Str "dashes:",Space,Str "one\8212two\8212three\8212four\8212five."] ,Para [Str "Dashes",Space,Str "between",Space,Str "numbers:",Space,Str "5\8211\&7,",Space,Str "255\8211\&66,",Space,Str "1987\8211\&1999."] diff --git a/test/lhs-test.latex b/test/lhs-test.latex index 3ca8f97c8..027ad3a0e 100644 --- a/test/lhs-test.latex +++ b/test/lhs-test.latex @@ -17,6 +17,12 @@ \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} \PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref \usepackage[unicode=true]{hyperref} \hypersetup{ @@ -61,12 +67,6 @@ \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} \newcommand{\NormalTok}[1]{#1} -\IfFileExists{parskip.sty}{% -\usepackage{parskip} -}{% else -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -} \setlength{\emergencystretch}{3em} % prevent overfull lines \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} diff --git a/test/lhs-test.latex+lhs b/test/lhs-test.latex+lhs index b0a58ac78..4aac4c7bb 100644 --- a/test/lhs-test.latex+lhs +++ b/test/lhs-test.latex+lhs @@ -17,6 +17,12 @@ \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} \PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref \usepackage[unicode=true]{hyperref} \hypersetup{ @@ -26,12 +32,6 @@ \usepackage{listings} \newcommand{\passthrough}[1]{#1} \lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{} -\IfFileExists{parskip.sty}{% -\usepackage{parskip} -}{% else -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -} \setlength{\emergencystretch}{3em} % prevent overfull lines \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} diff --git a/test/markdown-reader-more.native b/test/markdown-reader-more.native index baafb5334..1007dbac7 100644 --- a/test/markdown-reader-more.native +++ b/test/markdown-reader-more.native @@ -99,74 +99,74 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Author",S [[Plain [Str "col",Space,Str "1"]] ,[Plain [Str "col",Space,Str "2"]] ,[Plain [Str "col",Space,Str "3"]]] - [[[Para [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] - ,[Para [Str "b",SoftBreak,Str "b",Space,Str "2"]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2"]]] - ,[[Para [Str "r2",Space,Str "d"]] - ,[Para [Str "e"]] - ,[Para [Str "f"]]]] + [[[Plain [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] + ,[Plain [Str "b",SoftBreak,Str "b",Space,Str "2"]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2"]]] + ,[[Plain [Str "r2",Space,Str "d"]] + ,[Plain [Str "e"]] + ,[Plain [Str "f"]]]] ,Para [Str "Headless"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.2638888888888889,0.16666666666666666,0.18055555555555555] [[] ,[] ,[]] - [[[Para [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] - ,[Para [Str "b",SoftBreak,Str "b",Space,Str "2"]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2"]]] - ,[[Para [Str "r2",Space,Str "d"]] - ,[Para [Str "e"]] - ,[Para [Str "f"]]]] + [[[Plain [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] + ,[Plain [Str "b",SoftBreak,Str "b",Space,Str "2"]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2"]]] + ,[[Plain [Str "r2",Space,Str "d"]] + ,[Plain [Str "e"]] + ,[Plain [Str "f"]]]] ,Para [Str "With",Space,Str "alignments"] ,Table [] [AlignRight,AlignLeft,AlignCenter] [0.2638888888888889,0.16666666666666666,0.18055555555555555] [[Plain [Str "col",Space,Str "1"]] ,[Plain [Str "col",Space,Str "2"]] ,[Plain [Str "col",Space,Str "3"]]] - [[[Para [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] - ,[Para [Str "b",SoftBreak,Str "b",Space,Str "2"]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2"]]] - ,[[Para [Str "r2",Space,Str "d"]] - ,[Para [Str "e"]] - ,[Para [Str "f"]]]] + [[[Plain [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] + ,[Plain [Str "b",SoftBreak,Str "b",Space,Str "2"]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2"]]] + ,[[Plain [Str "r2",Space,Str "d"]] + ,[Plain [Str "e"]] + ,[Plain [Str "f"]]]] ,Para [Str "Headless",Space,Str "with",Space,Str "alignments"] ,Table [] [AlignRight,AlignLeft,AlignCenter] [0.2638888888888889,0.16666666666666666,0.18055555555555555] [[] ,[] ,[]] - [[[Para [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] - ,[Para [Str "b",SoftBreak,Str "b",Space,Str "2"]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2"]]] - ,[[Para [Str "r2",Space,Str "d"]] - ,[Para [Str "e"]] - ,[Para [Str "f"]]]] + [[[Plain [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] + ,[Plain [Str "b",SoftBreak,Str "b",Space,Str "2"]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2"]]] + ,[[Plain [Str "r2",Space,Str "d"]] + ,[Plain [Str "e"]] + ,[Plain [Str "f"]]]] ,Para [Str "Spaces",Space,Str "at",Space,Str "ends",Space,Str "of",Space,Str "lines"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.2638888888888889,0.16666666666666666,0.18055555555555555] [[] ,[] ,[]] - [[[Para [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] - ,[Para [Str "b",SoftBreak,Str "b",Space,Str "2"]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2"]]] - ,[[Para [Str "r2",Space,Str "d"]] - ,[Para [Str "e"]] - ,[Para [Str "f"]]]] + [[[Plain [Str "r1",Space,Str "a",SoftBreak,Str "r1",Space,Str "bis"]] + ,[Plain [Str "b",SoftBreak,Str "b",Space,Str "2"]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2"]]] + ,[[Plain [Str "r2",Space,Str "d"]] + ,[Plain [Str "e"]] + ,[Plain [Str "f"]]]] ,Para [Str "Multiple",Space,Str "blocks",Space,Str "in",Space,Str "a",Space,Str "cell"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.2638888888888889,0.16666666666666666,0.18055555555555555] [[] ,[] ,[]] [[[Header 1 ("col-1",[],[]) [Str "col",Space,Str "1"] - ,Para [Str "col",Space,Str "1"]] + ,Plain [Str "col",Space,Str "1"]] ,[Header 1 ("col-2",[],[]) [Str "col",Space,Str "2"] - ,Para [Str "col",Space,Str "2"]] + ,Plain [Str "col",Space,Str "2"]] ,[Header 1 ("col-3",[],[]) [Str "col",Space,Str "3"] - ,Para [Str "col",Space,Str "3"]]] + ,Plain [Str "col",Space,Str "3"]]] ,[[Para [Str "r1",Space,Str "a"] ,Para [Str "r1",Space,Str "bis"]] ,[BulletList [[Plain [Str "b"]] ,[Plain [Str "b",Space,Str "2"]] ,[Plain [Str "b",Space,Str "2"]]]] - ,[Para [Str "c",SoftBreak,Str "c",Space,Str "2",SoftBreak,Str "c",Space,Str "2"]]]] + ,[Plain [Str "c",SoftBreak,Str "c",Space,Str "2",SoftBreak,Str "c",Space,Str "2"]]]] ,Para [Str "Empty",Space,Str "cells"] ,Table [] [AlignDefault,AlignDefault] [5.555555555555555e-2,5.555555555555555e-2] [[] diff --git a/test/tables-rstsubset.native b/test/tables-rstsubset.native index d9bb9f2fb..8b7ccdf76 100644 --- a/test/tables-rstsubset.native +++ b/test/tables-rstsubset.native @@ -54,9 +54,9 @@ ,[Plain [Str "1"]]]] ,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption:"] ,Table [Str "Here\8217s",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.1375,0.125,0.15,0.325] - [[Plain [Str "Centered",Space,Str "Header"]] - ,[Plain [Str "Left",Space,Str "Aligned"]] - ,[Plain [Str "Right",Space,Str "Aligned"]] + [[Plain [Str "Centered",SoftBreak,Str "Header"]] + ,[Plain [Str "Left",SoftBreak,Str "Aligned"]] + ,[Plain [Str "Right",SoftBreak,Str "Aligned"]] ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] @@ -68,9 +68,9 @@ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",SoftBreak,Str "Note",Space,Str "the",Space,Str "blank",Space,Str "line",SoftBreak,Str "between",Space,Str "rows."]]]] ,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.1375,0.125,0.15,0.325] - [[Plain [Str "Centered",Space,Str "Header"]] - ,[Plain [Str "Left",Space,Str "Aligned"]] - ,[Plain [Str "Right",Space,Str "Aligned"]] + [[Plain [Str "Centered",SoftBreak,Str "Header"]] + ,[Plain [Str "Left",SoftBreak,Str "Aligned"]] + ,[Plain [Str "Right",SoftBreak,Str "Aligned"]] ,[Plain [Str "Default",Space,Str "aligned"]]] [[[Plain [Str "First"]] ,[Plain [Str "row"]] diff --git a/test/tables.muse b/test/tables.muse index afdccd476..fdf20be49 100644 --- a/test/tables.muse +++ b/test/tables.muse @@ -1,46 +1,46 @@ Simple table with caption: -Right || Left || Center || Default -12 | 12 | 12 | 12 -123 | 123 | 123 | 123 -1 | 1 | 1 | 1 -|+ Demonstration of simple table syntax. +| + Right || Left || Center || Default + 12 | 12 | 12 | 12 + 123 | 123 | 123 | 123 + 1 | 1 | 1 | 1 + |+ Demonstration of simple table syntax. +| Simple table without caption: -Right || Left || Center || Default -12 | 12 | 12 | 12 -123 | 123 | 123 | 123 -1 | 1 | 1 | 1 + Right || Left || Center || Default + 12 | 12 | 12 | 12 + 123 | 123 | 123 | 123 + 1 | 1 | 1 | 1 Simple table indented two spaces: -Right || Left || Center || Default -12 | 12 | 12 | 12 -123 | 123 | 123 | 123 -1 | 1 | 1 | 1 -|+ Demonstration of simple table syntax. +| + Right || Left || Center || Default + 12 | 12 | 12 | 12 + 123 | 123 | 123 | 123 + 1 | 1 | 1 | 1 + |+ Demonstration of simple table syntax. +| Multiline table with caption: -Centered Header || Left Aligned || Right Aligned || Default aligned -First | row | 12.0 | Example of a row that spans multiple lines. -Second | row | 5.0 | Here’s another one. Note the blank line between rows. -|+ Here’s the caption. It may span multiple lines. +| + Centered Header || Left Aligned || Right Aligned || Default aligned + First | row | 12.0 | Example of a row that spans multiple lines. + Second | row | 5.0 | Here’s another one. Note the blank line between rows. + |+ Here’s the caption. It may span multiple lines. +| Multiline table without caption: -Centered Header || Left Aligned || Right Aligned || Default aligned -First | row | 12.0 | Example of a row that spans multiple lines. -Second | row | 5.0 | Here’s another one. Note the blank line between rows. + Centered Header || Left Aligned || Right Aligned || Default aligned + First | row | 12.0 | Example of a row that spans multiple lines. + Second | row | 5.0 | Here’s another one. Note the blank line between rows. Table without column headers: -12 | 12 | 12 | 12 -123 | 123 | 123 | 123 -1 | 1 | 1 | 1 + 12 | 12 | 12 | 12 + 123 | 123 | 123 | 123 + 1 | 1 | 1 | 1 Multiline table without column headers: -First | row | 12.0 | Example of a row that spans multiple lines. -Second | row | 5.0 | Here’s another one. Note the blank line between rows. + First | row | 12.0 | Example of a row that spans multiple lines. + Second | row | 5.0 | Here’s another one. Note the blank line between rows. diff --git a/test/testsuite.native b/test/testsuite.native index fa234dfc2..0587bddb8 100644 --- a/test/testsuite.native +++ b/test/testsuite.native @@ -369,8 +369,6 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa ,Para [Link ("",[],[]) [Str "Empty"] ("",""),Str "."] ,Header 2 ("reference",[],[]) [Str "Reference"] ,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] -,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] -,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] ,Para [Str "With",Space,Link ("",[],[]) [Str "embedded",Space,Str "[brackets]"] ("/url/",""),Str "."] ,Para [Link ("",[],[]) [Str "b"] ("/url/",""),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."] ,Para [Str "Indented",Space,Link ("",[],[]) [Str "once"] ("/url",""),Str "."] diff --git a/test/testsuite.txt b/test/testsuite.txt index f6b0a7c95..9413cc81a 100644 --- a/test/testsuite.txt +++ b/test/testsuite.txt @@ -621,16 +621,11 @@ Just a [URL](/url/). ## Reference -Foo [bar] [a]. - Foo [bar][a]. -Foo [bar] -[a]. - [a]: /url/ -With [embedded [brackets]] [b]. +With [embedded [brackets]][b]. [b] by itself should be a link. @@ -659,9 +654,9 @@ Foo [biz](/url/ "Title with "quote" inside"). ## With ampersands -Here's a [link with an ampersand in the URL] [1]. +Here's a [link with an ampersand in the URL][1]. -Here's a link with an amersand in the link text: [AT&T] [2]. +Here's a link with an amersand in the link text: [AT&T][2]. Here's an [inline link](/script?foo=1&bar=2). diff --git a/test/writer.asciidoc b/test/writer.asciidoc index 2bf62e36f..639663743 100644 --- a/test/writer.asciidoc +++ b/test/writer.asciidoc @@ -600,10 +600,6 @@ Reference Foo link:/url/[bar]. -Foo link:/url/[bar]. - -Foo link:/url/[bar]. - With link:/url/[embedded [brackets]]. link:/url/[b] by itself should be a link. diff --git a/test/writer.context b/test/writer.context index 04df66178..9884c82c9 100644 --- a/test/writer.context +++ b/test/writer.context @@ -6,19 +6,28 @@ style=, color=, contrastcolor=] + % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option=bookmark] \setuptagging[state=start] + % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] + \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted -\usemodule[simplefonts] -\setmainfontfallback[DejaVu Serif][range={greekandcoptic, greekextended}, force=yes, rscale=auto] + +\definefallbackfamily[mainface][rm][DejaVu Serif][preset=range:greek, force=yes] +\definefontfamily[mainface][rm][Latin Modern Roman] +\definefontfamily[mainface][mm][Latin Modern Math] +\definefontfamily[mainface][ss][Latin Modern Sans] +\definefontfamily[mainface][tt][Latin Modern Typewriter][features=none] +\setupbodyfont[mainface] + \setupwhitespace[medium] \setuphead[chapter] [style=\tfd,header=empty] @@ -778,19 +787,15 @@ Just a \useURL[url4][/url/][][URL]\from[url4]. Foo \useURL[url13][/url/][][bar]\from[url13]. -Foo \useURL[url14][/url/][][bar]\from[url14]. - -Foo \useURL[url15][/url/][][bar]\from[url15]. - -With \useURL[url16][/url/][][embedded {[}brackets{]}]\from[url16]. +With \useURL[url14][/url/][][embedded {[}brackets{]}]\from[url14]. -\useURL[url17][/url/][][b]\from[url17] by itself should be a link. +\useURL[url15][/url/][][b]\from[url15] by itself should be a link. -Indented \useURL[url18][/url][][once]\from[url18]. +Indented \useURL[url16][/url][][once]\from[url16]. -Indented \useURL[url19][/url][][twice]\from[url19]. +Indented \useURL[url17][/url][][twice]\from[url17]. -Indented \useURL[url20][/url][][thrice]\from[url20]. +Indented \useURL[url18][/url][][thrice]\from[url18]. This should {[}not{]}{[}{]} be a link. @@ -798,41 +803,41 @@ This should {[}not{]}{[}{]} be a link. [not]: /url \stoptyping -Foo \useURL[url21][/url/][][bar]\from[url21]. +Foo \useURL[url19][/url/][][bar]\from[url19]. -Foo \useURL[url22][/url/][][biz]\from[url22]. +Foo \useURL[url20][/url/][][biz]\from[url20]. \subsection[with-ampersands]{With ampersands} -Here's a \useURL[url23][http://example.com/?foo=1&bar=2][][link with an -ampersand in the URL]\from[url23]. +Here's a \useURL[url21][http://example.com/?foo=1&bar=2][][link with an +ampersand in the URL]\from[url21]. Here's a link with an amersand in the link text: -\useURL[url24][http://att.com/][][AT&T]\from[url24]. +\useURL[url22][http://att.com/][][AT&T]\from[url22]. -Here's an \useURL[url25][/script?foo=1&bar=2][][inline link]\from[url25]. +Here's an \useURL[url23][/script?foo=1&bar=2][][inline link]\from[url23]. -Here's an \useURL[url26][/script?foo=1&bar=2][][inline link in pointy -braces]\from[url26]. +Here's an \useURL[url24][/script?foo=1&bar=2][][inline link in pointy +braces]\from[url24]. \subsection[autolinks]{Autolinks} -With an ampersand: \useURL[url27][http://example.com/?foo=1&bar=2]\from[url27] +With an ampersand: \useURL[url25][http://example.com/?foo=1&bar=2]\from[url25] \startitemize[packed] \item In a list? \item - \useURL[url28][http://example.com/]\from[url28] + \useURL[url26][http://example.com/]\from[url26] \item It should. \stopitemize An e-mail address: -\useURL[url29][mailto:nobody@nowhere.net][][nobody@nowhere.net]\from[url29] +\useURL[url27][mailto:nobody@nowhere.net][][nobody@nowhere.net]\from[url27] \startblockquote -Blockquoted: \useURL[url30][http://example.com/]\from[url30] +Blockquoted: \useURL[url28][http://example.com/]\from[url28] \stopblockquote Auto-links should not occur here: \type{<http://example.com/>} @@ -871,7 +876,7 @@ Here is a footnote reference,\footnote{Here is the footnote. It can go indent the first line of each block.\stopbuffer\footnote{\getbuffer} This should {\em not} be a footnote reference, because it contains a space.{[}^my note{]} Here is an inline note.\footnote{This is {\em easier} to type. Inline - notes may contain \useURL[url31][http://google.com][][links]\from[url31] and + notes may contain \useURL[url29][http://google.com][][links]\from[url29] and \type{]} verbatim characters, as well as {[}bracketed text{]}.} \startblockquote diff --git a/test/writer.docbook4 b/test/writer.docbook4 index eee19cdd9..163255974 100644 --- a/test/writer.docbook4 +++ b/test/writer.docbook4 @@ -1249,12 +1249,6 @@ These should not be escaped: \$ \\ \> \[ \{ Foo <ulink url="/url/">bar</ulink>. </para> <para> - Foo <ulink url="/url/">bar</ulink>. - </para> - <para> - Foo <ulink url="/url/">bar</ulink>. - </para> - <para> With <ulink url="/url/">embedded [brackets]</ulink>. </para> <para> diff --git a/test/writer.docbook5 b/test/writer.docbook5 index 07ca0f827..992cd8b63 100644 --- a/test/writer.docbook5 +++ b/test/writer.docbook5 @@ -1224,12 +1224,6 @@ These should not be escaped: \$ \\ \> \[ \{ Foo <link xlink:href="/url/">bar</link>. </para> <para> - Foo <link xlink:href="/url/">bar</link>. - </para> - <para> - Foo <link xlink:href="/url/">bar</link>. - </para> - <para> With <link xlink:href="/url/">embedded [brackets]</link>. </para> <para> diff --git a/test/writer.dokuwiki b/test/writer.dokuwiki index 79fcdde8a..4ba1b7054 100644 --- a/test/writer.dokuwiki +++ b/test/writer.dokuwiki @@ -556,10 +556,6 @@ Just a [[url/|URL]]. Foo [[url/|bar]]. -Foo [[url/|bar]]. - -Foo [[url/|bar]]. - With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. diff --git a/test/writer.fb2 b/test/writer.fb2 index 0412c8cf4..63d0bdfbf 100644 --- a/test/writer.fb2 +++ b/test/writer.fb2 @@ -1,3 +1,1013 @@ <?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>foo<p>And nested without indentation:</p><p>foo</p>bar<p>Interpreted markdown in a table:</p>This is <emphasis>emphasized</emphasis>And this is <strong>strong</strong><p>Here’s a simple block:</p><p>foo</p><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>foo<p>This should just be an HTML comment:</p><p>Multiline:</p><p>Code block:</p><empty-line /><p><code><!-- Comment --></code></p><empty-line /><p>Just plain comment, with trailing spaces on the line:</p><p>Code:</p><empty-line /><p><code><hr /></code></p><empty-line /><p>Hr’s:</p><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>——————————</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">/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAD6APoDAREAAhEBAxEB/8QAHAAAAAcBAQAAAAAAAAAAAAAAAQIDBAUGBwAI/8QAPhAAAgEDAwIEBAQFAgUFAAMAAQIDAAQRBRIhBjETIkFRB2FxgRQykaEjQlKxwRXwFjNictEIJEPh8SZTgv/EABcBAQEBAQAAAAAAAAAAAAAAAAABAgT/xAAbEQEBAQEAAwEAAAAAAAAAAAAAARECEiExQf/aAAwDAQACEQMRAD8A2t0YoQpwT2qVzMV+N3UHgrDY2eoM0y58VEbgfp9K1yMRmnuJ5h40jyYHGSeKrWE8u2QAApOMdqGCsmT8h70TAJwMAZx249aKBy4c9vTNUC0zDCgmmmG7Ockjkj1PrUTAjcy5XP0ouCgHae4IomOJHhgIc55PHY0Uk5IXLMcUBQ27n96JYO2MYLebHtRBA7BcMx29sdxQJqwZRtIP+BQKpjHHc+xzigNGoAO/k+nPAoAYlee5oBiGeWySO9AJCgY5PHagFCADzj2GaA2N2TkjA/U0HMwbPPeiyBLDfkkj04FCl1cBMgn6URwYFGySR6D2oAeQDAxnHGKAhU4IbGc+tFwnwDj9aK7f8v2oNu+IHxNvJdXmt9EmKWSqArA/mPvxUxMZNe3Ml1dvNcMzSSEsxPOferJhht/OWyAPc0UfdgDcuM8n50AMCykZFARsngcY/egTcbjnJz9O9AB2kZGSQOcUCX8x83bntQCMruJ4B7D1oCyOGzxtJ9M80CAdg5UjFE0aFJrghLeNpHY4IRdx/QUNWCw6D6q1EZttEvirHAZ4ig/U4qw1b9H+CHVN3Mq6hJaWMJ5ZjJ4hA/7R3P3q3ET+pf8Ap/lWNm03XkkkA8qTW+3PHupP9qxopV78G+s7VSV0+OcAn/kzqSfscVvIKzqPTWu6XKE1LSL+Bhz5oDg/cd6lEZzGwLrtPqrA8frUCJfcw9gfegUjZsEAffNADyHt78UAjCjzDJxRcO5Pw3gwCGOVJQp8ZncMGOeNoxwMY96GCbQffFFcUXKjDDt2NEo+N3yyM5z3okKuqJgIzONoJyuMGi4QfGcqSfXBoYHJx659qKIRnnsfUGgJn/poJYoTIGLY+eDzQFlQK2G/KCTmgbspfO0qce/agPGcR7nHf9vnQFfBPlOc88Gg7uucc/M0Bd208YJJweKAYrea4kKQICRGW5IUYUZJ570DYqcknt3FE0VuVyDzj1oamOlulda6puvC0a0eZVIWSbtGn1Y1NNbX0x8ENH0qL8X1NdtqDoNxiQbIh8u+WpqL70Tc6fcxypouiRadbW8hhLFFXcB7Edz+tNFvEZxkmmgShbA9PlUA+Hgg/wBqDgmBkd6ArJuJBGR7VdEdqWgaVqMfh6hp9pcLj/5Ig2KaKJrvwW6S1EFoLaWwmPIe2fAz81ORTRm3UfwI1mzBbRL+K/ReyS/w3x/b+1Wexmev9O6xoE2zWdOubUDszr5T9G7H9auCJj2n3PPrUXTlGBB2kYx96GlQMjJJHuRRXBgDgk8DtRKH8w4OfYA0SUlIMsFXJ4oujHH8ufnRRGOSNoJNAeFC77F2jPucfvQFEqgY3nj/AKaCUY58wwq54AoCzOmVMke9QeRnGR7ZoEIF7pnaTk49KDpSSwQntQJsGKjgggZ9uDQc4OOe1Am2UCkHOR7dqA8t/cSW8MEkrGGEsUTPCk4zj9KJT3pzQtS6m1aPT9Jh8SVxlmJwqL/UfYURuuhfArR7f8NLrF1cXciKDJCrbI2b7c4+9NGtaRptrpdqltYW0VtAn5Y41wBUodvGjqUdQyn0YZqAIreOBFSFFRF7BQAKA1xcRwKplcJuOBn1NAR7y2ikWMzoZnGVQHJNAuQcD3oBKkD2FBy8jnvQFxnjjmg4rxwKBMqCBtPNA3vbCC+tngvYo54HGGSRQQR9DV0Y91n8DNOvFkuOmZmsrk5PgSNuiY98D1X+1XRhWu6DqWgX72er2j2069t/ZvmD2IoGG7jbnj1FFlB224PB+VClN4DYJHyAojmPGCck8cetCAxgjPp6UaAGKtx6+9ATAXO7nFBw8HHLN+goJhBuj2FeAcnmgNazW8U0vjweODGyqpYrsYjytx3x3oGa5LEEjH9XvQGlgmjjMmQq4HBPfPYgevagG5nhe3tkFuInQHxJQTmQntn0wKBKTlAeDx60DSY+U9zn+mgsnQvROr9Y3W2xi8KxV8SXUnCrjvj1Y/IUR6c6A6H03o6wMVgrSXMoBmuX/NIf8Djt/eiLfjJwO9ZBiOfmKDhktzQAzYBLZ8oyaDF+rOptVv8AUjNZL4tjA/lT+kr3wvqTQX/pi3Y+DqFxKXurmFWAaPaVzg4I/b0oHlxqV7penRTXFu93dPLsESYB2k8n7CgnradLq1WaIOFI/K42sPkRQCg3Kcd6Dgp3d6AdrGg5VxnjmgKWB8uQGxnFAUgKuSefSghuqNC0jXbAWGtxQyJKdsYc4YMf6T6GtDzR8S/hnqfSUz3NvuvNILYSZR5o+ezj/Pb6UGfLzyD/AJoFFySQVBHpQDJ5kGByPahAbWxn5+po0OF3D+XPtQJsNwOe+aAuygmMkebgHnHFALHYpJwSeGz2oGpOJWAI49BQEZlYAHkg4oARVOMvtBIJJ7AUAX6xxSOsUgmjViFcKRuHviiVfvhT8NZuqpk1LVFeHRkPlHZpznsP+n50qPS+mWVppdnFa2cEcFtGu1I41ChR8qyHVxK8cLPDD4kgGVQHBNAa0maaBJGTYzDJXOcUCy5JOaA2OMfoaArkheM7vlQNYNOtoWLJCgLHJwo5NApPKLaNpGRQB6j2oGmnRvcyNd3O/DkeErLhkWgklIdCyZOCRzxzQEeRxhdpUnncBkD5UCxXjJ7+tAlctMsIMLohz5mcZAH09aBQYdQwyAeaAuA7MAQxHH0oG1481nbGVInuWU5Kr+bHrgepoKB1u+o6jqlvBH05NevEBPBK0pQR4I4BHZj+1Bb9IS7lsFtNWtYwDGFYB/EXHqpJ7/WtQYx8VfhGbdZtV6Uh8gy81mpyR6koPb5UGKY4YkeYd88fbFAI5AC98c5oQBb+U9+9GnN5RgDgjOPWgAN3yMfWgAqc91/UUD2RSSRg9+49KCR6e0WfX9WS0icRwgb55WOFijH5nP0FBYNRi6dSR7HRNPmu0hOW1GaXaZMdwBwAP3oynE0XRYrFtV02wS4ECj8dp1wcsE7eJEf39qlFZ616ZttPu7Kbp9Zbi0vYzNCcgjHqoHuKsEp8LPh7P1PqjXerxywaXaviRSu1pWH8g+XuflQemIIY7S3SK3hVIo12pGoAAA7AClEL1N1RH0/oTalcwx+IACLaSQKx59Ppmshv0D1jH1ZbTubU27xkkAnKsuSMg/UUFluLlLaJXETyecKAg554zigXiubeRnSKeJ5FOGVXBIPsaBLULoWkIfw3kYsAqIOSTQJMbpm3oqlmwACeF9yfn+1A+Bx34oE5IY5P+YFbnPIzQKAckHuRQCAQOO1AL8r9KDhkZOT9M8UCcrxgAyYJzwD70CT3Itxm8kgi3fly+P7/AOKA9pskhEkZysnOfeg6RH8w3tgjAHtQRZ1uystSg0m5eRJ2UbHceV8fP3oJkBSAVII9xQFdSRwKDDvjN8L/AMSJte6chxcgFrm1QcSf9aj39x61YMH8Q+CkfhqpQncxBDH5H6VRwXJ/Ke1Am2QchuMYOaNFSAVznB9qAm8f10D2RmX8jDHP3oLbebtA6ej0m2LrfX6LcX7IMskf8kf6HcffIoG8yTadZxSTxCK3kRZUwSFfkruIJ78GhiS6Y1OS3160uZJFWO5bwZtxzuQ8bcfPNMZXvo2wsLnQ9R0q/maJNNv5Yo3bjCuMAHPzqA2jdUan0lF0/ZXcElxp9zE+5WVd/DE71IPPB7H2po1bSNXsdYthLp1ykyEcj+ZT817ioITrnoux6vs1gv5JYnjz4ckZ/Ln5etA+6N0BemdBttMina4WEFfFdQpIJJ7D60E5I4Vo9qnnsQO1A3k0yzeTxhCizZJ3qNpz9RQO449igMSxHGW5NAIwBtUAUAMORkfegMhG3jtQD8+fvQGXJz7UAHuRQA5YDI5FB0qCQA5yaCs2/SFit/Jd3AmvJ2bO64ctt5zwD2oLMilVAUDgcAelAJLbhgZz3oGN9HPIYmhtrWRw2czjt7Y+dA+h3mJS67W9gc0AvuLYANADpkZABHY85oPOnxy+Hx06Z+odGjC2jt/7qBRwjH+cY9D6/OrKMebcceHwfaqCYIyDgZ96GhHOFJI4/WjQpXnsaCz9J6fDqGvRC8OLO3Vri5PB/hqMkfc4H3oDT3UupapcXrKS9zISgDdhnAGPbsKC5aLLBHq9p01c6bbagPE23kpJYhmz5IySAAMj6nNGdRnT2lu3V9vaQQrJDHfCMFj5kAfufsMUFogu5H0jrLUYXK+Lq0aRse/lf/8AOKlFfudagvbnQpNQRmtILydCwPdCQcgMOMZFQanPoeiawBd9M6s9jeKPK1vKQp+RFA4septa6fuFtuqbRrmzx5b+BAdo927A+vsflQXfTr2z1O3W5025juIW/mjOR9KAZI914khaRNo4XdwT9KAl3b2+oWpjMoZWbOVfnI9sUCrXUNssUU8w3sQoJH5jQLvwQQC3NAKvuUPtK54waDg23v6UA7weBnNAIOBigMr+hoOjdZQdhBx3waAVG0Z7UBWfAOQSflQChyNxBAxQRutarb6bHALi9trSW4kEcJnGd7ewFA/j8QEK/IA/MBjmgWDDBB7igj9dupLTTbiaHZ4oQ7A7bQW9ATVgwXSNV6onl8azW6t45pWdxHIxWA/zNtz7A8Glg2S1u7fX+nt0J/H2c4MMhmQoW9GBUjj60g8sfEHpebpDqi4sHLG2Y77eQ486E8fcdvtVFekGW4UfegKVAAKgnFGhuDzxQXbpDTZF6a13UnUqrCOzQ5wGZmXIJ+lE0ppkEK6nJcRWcTW9hA08iKcjcowpye/mxQ0+6VRbC/jvLm48L8LG9y8pIOXxkDnuSTipqHXQMng3es9S3fhn8DbvcZI5Mr/lH700dc3Dad8NtPs4nU6jeXD6nMCwBRF5XOfU8YHrTNJFF1X8RawW1jc4GxTKNrZB385yPkBTFw1stSu7Ni9tPLGSQfK5Aphi8J8UNUm6fn0u72yvJ5fGbuF/39aYYtGgadp9/axXnRetzaXqnhqZI3bEcj4547Ak/X6UxFisPiXe6NMdO65057eQAr+LhUlHHbOPX07UwWXpQ6BqMo1LpgW0sioVI8Qgxk+684qC028M5890Y3kHKbUwF+lA4LDOzu2M4FAOG3DaoI9cntQdJxzQEyR259f/AKoGl5fSQRFo7ZpB/MhYIR9zxQdayyXKb7gqox5Yo2yB9WHc0DPUIWnhWKxkuYFRs5gcKWbPY59KBkx6isVeSGW31JNwHhyOUkA+o8v9qCfjkMo/LJFKqBmRvSgc2swnRyFcYODuXGfpQMtRsLK8vYJL+wjuGiUtHK6hghz6Z7H6UEmCsig84I9RigiruC9t0DaaVmIIHhTOQMeuGwT9qCJ1ywv9T0U29xFFiaVBJGHz5M5ODgYPY/arKJPTtLW1t44i7SKq48w8x+ZPrTRJoipGFQAAdgKgzX47dMJrXSrXkUe6807MykDkp/MP8/aro80FQyZ+tUJ7hvH0x270XQ7KGtXvIk0T4c9P2bIhkvpnvJVfjIxhf7qftRDXpu0/1DpzXltUlkvmWMBI+2zdnn64oYa6yX0XTm0i4jQ3t6wmuV53xov5UPpyeeKyLbpFtZ6Xpmn6TqNq7/ic6pqQRR/DVf8Alq2fTOP0FXBnXU+ox32o3lzeW+JrxlMXHKR9wfbJ/tVWK5f3AnaAjafCTwwcY4BOM/qKKSjA4Dg8j37UHZKkE5P0olSFlcLDdJPbTNBOigjxOVZu3+80Rbbnrq9l0t9I6isRd2rgKpPlZMdyre9An07oupoh1zo2+lea2fMlr+WZFx7ZwwqWDVug/ihDq7R6b1EPwmpMNokPlVj8xng1BqEUe1EAJOMDOc5oDSxq6YYeuaAJF4oCBUQ7mJ45zQHYB14wR86AVjBXyjge1AEcRTHlA9hQE8kbgEohJ5yQM0ETHNqMOr3IZQ9tIMQyEjKt7D3FBLqywRPJKTuxlj3zQI3Ut14e+yhWRj28Q7RjH60EfpF3rU/jLqFrHbS4/hqpJXH19aCRa8jgiVr1xGwXzYyf99qA9tc29/aRXFnKs1vINyOO2KByoxwe9AYocHGKBvdwLcWzxSLuR1KuD6gjBoPHXWujt071Nf6YSSkUnkJ4yp5H7f2rQgWAA3Y+1An4j/1t+tBrHxKuYS+gx24LRx6ZFtI/lz60FY0+/v8ASphNpd68EpXY5AGNvzFF1YOirZbzVrvX9dkNxZWH8eeaY5Lyj8qj7kcVlETqOqXd/HrPUNzcNE16Tbwxf1JkEgD2AA/etBte9R2Oq2cv+p6XHJfBFjgmjkMaRgAAEqO5o1FWfbgjsR8+9AlI5CgEggeoNAq0iug8uD7g80KKmCcZ7fPmjJzJfT/hWtjJvhOPK/OOe49u9A96X1W90/VrRtNkkSfxQF8I5yScdvX6UGidSLpfVFzcvbRiy6kgZBGysFW7B9T7HHNSjU+o9S1iz0e2uNLmX8RYxJ+KgYeVwVGTn5d6gjug/iU3UOt/6TewQpP59skL5B29x/8AYoNHPB78Ggb2l3bXO78PKsoyVyvIBHBFAoSkbfyhn4GfWgTnmWFN7ybAvc4Jz9hQRdx1dp0S3Dw+JJHbDdPIUZUjX3yRz9Bmrgzbqb4x9Oxho4bB751O5HPkXPsc80wQHT/xrJ1IHUbGKO0kdRiBiAgz+YjnsPpTKN/tLy3vLOK5t5klt5F3LKhyCPemAYLuK5XMDEj1OCP71ArGWLMPT0oIbU7h11u2t49OllWWNm/FIRsjI4AI/egfQ2ktpbww2XgxoDl9wJ49cUCHUGv2GixM13Mkcm0squwUH5/SrgxDW/jFcXOteHb3otrKEEiRISRM3zGc49v1qDT+gfiBpvV7y2unxTxywRhz4ozuHbOR2+9Bmf8A6kNIEWpaZqiooEqtBIR6kHI/atfRjDEt3AKjgVQjug9j+lQWh72e/htTOzyeCnhHPomeMYoJvQum7vVD47K9jpsQBkvZ5NoAHcgUAa7rKamE0Lp9Xh0G1OZZTwZSO8jn9cCsivdS38F9cJDZIY7G2URxKe5x/MT7nNaEKrENwAFPPlosFwS2cd/cc0UlIm3JOeKDo2LH+UA0SjgDk98URzPiJ2449e/NAbS7v8PdpKkpikQ5WQLkqccGgmYNQmXWLeQLG9wVRQVPlcj+Yn3xQa98OviAjz3WjdXSpFdliEuJCNjDtsJ7enepRdel+kdL0rqOTVdIsoYklV1dixO3nunpg9jUCnU3WMeka5b2EUcl3JInmigQs6ZPlJAHY8+vpQP9O1m3nthNo0cTwM2JDwoVj6H5gd6CbhtUiVn8TcXO4ktkZ+We1BAf8Z6fZ2uqXWpyxQrbStGseQzMB2IA961B59+IHXmodXal+HsPFh04HbHCo25+bY/zQWv4f/CCxvII73qC8iuXYb1tYZeF9txHf6U3BatX+DvSl86x6cr2dwjbnEUmcj6Enj6U8hLdJdEX/SmowJp2tTT6Oc+La3HO0442+3NBf1LmRUjjQAfmc+nyHvWQockYyQcY3CgaabaPZxGNnaUFi3mPb6f+KA2q3RstNurnBxDE0mPfCk1YPMemaP1L8RtYN9fJPc2aMUaVmCKg54H0z6VRYendf6Z6T1W56a6j6fgfwJyguhGJmPzbIzjHtSjTn0zSunbi01fSkt9Os5GAmWNCDOGxtXb6HnNZEZ8etOF90DPKFy1rKk3zAzg/3rXI8u7zvOTg4zVoTLDJ81QWDTb2SwuvFgcrkbXwM5H0PFGqsjpd6+kcT61Nc2ieb8OikFc/9PA+WfSjKA1nWBzpFlZ/hLWM4KH8zsPVj6mghN4IyQRk5NGo5BkFmyAfSgVjChdpGO/FAXYpOHLBe/FAQqoBJbA9sUBGxgtgEj/eaCf6DGjt1TZf8RNGumKS7mQZQkDIB+WaMrf8Ub/ovV7V20JIYL62K4khhCLOCcEcAdu9BmCuEQvxvyFUg42+v+/rQaj0zax/EXRY9Nns0t9TtM+BqCKAjEclXA98jn+1Si7Cz6u6O0tLjTrxLu2tQJJrDwcKE/m2M2SfeoLrpupDV9Mh1OytUS2vIN8m4BZQf6T7+vPpj50GfdK9L6rJqk1y1y0elRDKRqdjHHoyDhjx39e9BZr7fagW0j3kul3iETRqHkeF8ZBUjkZIxjtk5rQ86dW6r+O1OcW0UtvaRsY4oWfLKBxz7k/5NA46P6X1rqS6WPS7V9v88rAqi/f3oN46X6C1DSotkus+BIwKl8hn2+3PapROXPT2t20bPY6kJ5UGYmbIfIHGW5z68VBI6DrzzWSrrAjtrwFUbDja5OBlfuaCbluJLeNwIpLiVF3bVXAP0Pv8qBxLO8cYcW7vnuqkAigNFKs8CyxlwG/lcYI+1A31ayF/pt1auSFmiaM/LIxVgwfoO413o3qqfSLyUSwodogAyZVGcbPTPr71aNDvendJ6wtbu7Fi1lezK0bS4VZMjtnFZE0bC5u9Jh0qRAr2yw4uWx59vBI44PegN1tpbap0lqOk2sipLPB4aFsnHbBNOR5A1exFhqFxbeKkngyMhdOxIPcVuhiZFz/zBUEwcKvYnP6fWi0+6chjn6h062uATFLcRrIMnzAsO9EehNR+GvTV3GUh0+O2YsGaWHIf9amjIfib0no3S0VtFY3M000zMzLJtLKvvkenyx96oz0rwNjA8cj2osFLbVAbOc9jRQiXOAwxnj3oBlAxwDj37UDY+vHOQeTQBIdqjcPMfnQwJclWyBgCjJBFeefw4VaVycBUGST2wAKD0L8H9C1rSIILjWLSCytY1lZASVnlL4PI/wD8+vvUo1uwbxI5GkjdVc7isvOBjtj2qBWKFZiQ8CJCB5FHYj5jHFArDbQ20ey3RY1HOAOPsKCH1u61CPSLt9MtlXUHUrbCbJBbPdtvYetXR5T1y2udD6lni1ErJdJLvlK4wWOCePvVgsV/8Sr67UW1vA0NiowIonMe4+7FeT9ARQRmodWa9EYpPBhs1Tygw26rk9xknkn70EjonxZ17TXjAeKTkZ3L+YZ7N8vpSjX+lOpNM6umgkMG3EgBV1DYbG4kewz2NZGkC43CP8MPFBONysMAD50DaHVH8S6N1a+BaxMUjd280pA5wPb296DrXWLK9WNoJdtwybxDKPDcAnHIoJBifTBzzmgaz2UFzPFNNbwvLCcxuyglT7igdRRKg8qAZ5JAAzQEnuYoHiSWQIZW2ID/ADH2H6UERr12BY6hueIQJaO7SK/nHfnHtx3pyPGWoN4jynuCfU963RF+DL/UtQWTkjaWY/8ATnHFGql+j1VerdJY8r+KiJz/ANwoy9C/EjqSbpbRY723RJC8ojIcZ4IJ/wAVkecer9en1+9FzeLCCq4URjgDP7mtLhteadBY2kMczyHUpcO0YxtiUjgH/q9celAiLy1kjCX1ruyMLNGdrj0+h+lE0+t+kNQltJ7yKS3jgiTxUFw/hySp7qp70NV6YEBgWUNjBoaKeAODnHrRoVgDnBP0ozpxZ2f4y5trVeGuJFiBPpk4zQep9C0LTembS30fQbWP8ZsDyTugZgf6ix9fYZpbgmbXSmXULaa6kMzpltzcjJ//AGpaLCY1CDsF74PrUCgHY0HbSx7Z96BGUfxB2xjtQZ11t0Tb6jNfyw2wM18gV5AcBdpzyPnV0Yp1F0o/TEczXjXaTOQYpIk3QlT3B9Rj0zVl0VKbVppImheUSwbsgFfXHc0De0tri/ujFYQSSyfmKopPHqaDV/g9p+padr/gkSRTzKu0kZRlPLYPbOPf2pg9GWzRCMJAFxH5do4wayKX1z/G0CdzqLWRkiaTxQBLudclQvovbv3oMU/4Z67uwnUAt3u1Zw42yhmx3/Ln8v0oN86L1d00i3i1UiGQIocNnEbnkqT2xgiguEbI4DIysp7EHNAZnxQQ/Usksej3EsCl5EUthR5sY52/Mjigr6Qrp3R15LqEcIlmgdpFGAsY2navPJApyPJtwd8rnGBuJz6Gt0MzGSTyf0qCwSKA5ZsAjnn2otTXQYj/AOMNIDqCrXUZwf8AuGDRG6fF6Gyfo6+ub0CR4EPgIScLIeAcfc1keatN0661a+S3sYTPKzAbV9B7/StLrQ/iXp9pYLp8elWsUM11AzXMqt53I7g7j244oiB6W6Tn6j2TeAy2FspTeB+Z+/8AmgtnWlvpdl1Dp1pq07Ja20GFQpuDHHAwPf39KDHriVTKSPOCeBnHHtQGsrG5v5pfwcTOIlMjgEeVfck0XRIreS7uUigRpJXOEVe5PtRE/wBJ9HaxqvUcdhNFJp0lviaSWVcMgzxgdySeBipo9T6O8NppUJ1K4iW5KgSvIyqxb5jPH0paJm1NvKivE6Mp7MpyP1FQLRTwy58F0cjuAckfagOTtO3+Y8igMWCIWbOPlzQNhNBOWEbq5Q+YKc4+tAD7JEZgQfXj0oI/VtIttXsZLW5hRopByCP/ADVlGRa78Erae63aXK1tG3LAncM/Kmh10l8IZdBv4rxtTE0yggJsyoz6/P0po0zSNKEMdo9xGnjxuzkqMAEgjj7GmialjWQMgyCRyQcGoITqHT7q/a30+G2jFmwLSzl8GPBGFA9c5NBPRwJDbpHCipEi4CjtigqfWltqCaG8WhNbxyzOBIs8W8FcY4+dWQQ/wtuZdIGqadrknhy2u2QyOSEZOeRngY+XvTBZZevOmhC8janbqiZ53Zzj2FMFcs+sh1ZqsFrp8UkGkrlpbh+DNzhVX5Z5NQTfXyWUXSV2t+wW3EZ5I4HHt61eYPI0mA5C9snFaoLsPv8AvUEk5JcA8cZG480WnOlXX4PVLO4yQ0cyP244Yf8AiiPUfUump1B0/c2O8xfi4v8AmL3UcGpgw/SujNX0Trj8PpckimOMvHO/kEg9Rjs3PpV0aFq/Qqa1ZJ/qcrverEqNOwGM+uMfemiVtrKbQdMNjp9rvtkhPht6mU5yT8u1BkvXg1qXUtOvddgRY1R3j2YHiMvZSD27CgzSCyuNQ1KK0giL3Mz4VAOc/wDignoNNOnaHeiW8hgkku/Al2+Ziqgn09M0ETp0qpqSmGKOdFcEeLwMfPHag3JLuCRtPmQWsDhNphtVAcn1w3BPFSwDdWGpX1/OYdOtbbSrlQ80szHdn0GR24/c1AbWemdatLbTJdGvJIJypDQwMV3exwOPatSz9Ei/R+txy2mr3evyHV4miWIDhGwwyGA7nGRS2YNZAUBWYDdjGayEvxMYB3nYu4KCfU+woG93c2enWs1xcPFDCp8zEgDPzq4GGgz22saS1zZSZhkdsFePXt86YHWmySeLNDMYikZ4YNlvvUD+VARxQJqgwRQHUAAe2O1AWOFRM8mDlgB37fagb6reXFt4ItLZJnZsuWfaI0Hdjwcn2FAz0nWX1i4u4xY3VpFbv4eZ1x4vGdy/KgkriN2aMRlQoOW3DOR/5qwYr1P1tp2pdS3WnanKkGh24kRl2eaYgcb/AFxnnAqiv9Jno0dRLJPbtdQtkNPIALaMnODsPPpSjbdK0DTbWQXui+Gsco3BU5hPP5gPT7VkU74t6PZHpq/1N5ZZbwrtRnmOwDPOFJwPsK1xR5ybudw788VaC5X2WoJRULSBpAe5PA/aiinAZnHck4A70THq/Qr23/4Y0u4lmCpLBGA7epxjH60Du+WGOBvFlFuWOFcYyCfbPrUojri6k06xX8PFNfBUJ3ltzM3scVBjfVvVXVNit5dapNDZGQGK3shjeAe7YHIwAOT3zVggNTiu+orrR4p7m+upJFR7h3TPhggDaoBwRjnPH5hV0af0xotnoD3l5dWdrY2YjGLhwPEHoef996CC6m0HpuPpk3Wny2s9sJPHJ3AeI2D39T37UGU9QTDULuGPSLPwIyoVIYk2lj6/X70G2/DPp0hVudRuBLcwxhRGkeEjB9M+p96DSLprVHiieaAE+YxHkke4H1qUOIBawL4uAuc8nvj71AwjRtQ1eO78QNp9odyLju/qT8uf70Gb6r8SpLzryy0vp+4NxYSSCEswI2u2Rn3OOD9qC4dVamen9NlaC7tUaIFvCmnHiy4HJXJxnOeDVwed+rOvLnqSyWO4jZSru/kc7ck+30GKosXw2+KmqaDJDY3jR3OmqNoRhtZAP6SP7Ggtmt63qbTJ1XLazJpslwBFblypEOAA7L2OWANS+xrnR2vW3UmjJeWp8wJSRf6WHeoJdSPMCRmgMq8DmgBpNsgUIxBGSccD70DczmS8MDWoe28MN4+f588rj980CktuJZYpFdlKZ4B4OfegQ1hpIrVjbsRMBhBj8x9qsHnX/hm36y1O/u9V1ddPmS6aD8OkQdyxOSe4OMmqLUvwQsYY4Xjv7m4YEEhwFyMY7fXB5pRbvhp0jqfSMV7b6jqZvLGQAwxAEBDk54PuD6VkVb49a5DBpiaNaeF/FIaQDumOwpzMGDEZQZ5+VboR8In1I+9QWDY6gFn7jjHpQhtJEFbAGBnijT0P8H7qPVOh47a42yNaymPBOcDupoykep+m73V7g/8Av2itQowvJIx6j5/OpRjfUWrax051RPY6LqFy8YACkebO4Z7HjNWCY0ToW2utJbqPreW5na4O4R78cehY9+fQVKLX05p1ro97awC4kX8VFmJLeEKdoyfOxJPbHbHYVAz0rqKPWNauri9t1ktJgILYgEiNFJ/Op9STmrBBdeaFCo0y3jt444DI3jLE204Y5DD+9UPPhv0NaRtPq99mSLOy22nOfdh75oNC0vT7m1uJGvGiii3AW8UDbQAeDu9zUohLlhouqap1VciF4I1FtbxSthtobBIPuTn7VBJ2vUth1TYk2ULi9iALwyKQYz6Z9CM0Ft060/DWEcDHe2Mucdye9BFW3SekWt3LPb2cUTsd2UGCG9x7VYMzufhzdX/WmoXj+BPpx3I7XZMmXYckc8EVRKaP8I+nXikLQuxOQSTnBzj6UEjonw90XSrq3S3s7dplJcl1EhGDx396lFx1TQ4NVjaC7UNCU2lAO/8AvNWDCLp9X+E/WgWImXSp2LRq7eSRT3B9iP8AFSjd9P1+21TRodVsMS2rLmTbyUGOePXFQSltcLcW0c1vh4mXcjDswoDLdRm4FvISsgXeTghDzjAPbPyzmgVFxCzuiOC0WN3sM+5oDqySJmNg3rx7UFb60tNUubGJdFdEvhIdryflUFSM49TVgyTQenJemOorf/U4H1Fpp1edAh/hOQTuQ9375JA4q0bnbPBcxxT20wkjKkqYzlT9ayGWu38um2MbLEJ7iRtoUds+/wAgBzQeW/iHqi6j1PdzeL44HkL9txHtWhVUOVyvHNB2F9zQT8hUAhAdp5FCG0mSAzE4HGDRppvwL1bwOpJbEsFiuYyVX3deR98Zoy2ZtRgmjkSRZocEp51K7se3vUow/rfpFE124mVpfD4mk2MWdCc4A+VWCH1281/UmFnpklzPYRFBEG8uGC4yQfcn9alGgaJo95rRsbi53WaxwrHOm7BjYcHnvz/moJaw07pXSI5IW1K0CQnDhpAWB9R796CudY62msTRW+gadI8KnDXMkLLv9MA8HGOKC3dAXF1dRfh75f41moi4G0AdwcfTj7UFhv7RjqMBV5AJFZHkR8FRjIx6CgpXVNjJ1JqNn07p26CztSJLlpIydyj2J+fGaC+afplrazqLa3SKNIggx3IHYUEsBk4wQc4oK11L1z070/M9rql6wuVA3QopLcjNBDwfFboqeSO2W7kQNxuaEhQfnQLt8TuireVoV1UeXnckTFT9DigHRuv+mbu9ZV1W3Nyc4IRlVl9O47/KgtU+s6baw+JcX1umRkAuM/p3oK/1t0rYdX6cqXKESqN8Ug/Mp9P1qwZ702mo9GdUTWJsmOn3EY8CAORGXJAwScjJ5q0bJDNLb6YklxbKsgA3wwndg9sDtWQN3aw3ZKTwLLEQOGORn6ehoG1vYAw3FikRt7JSuH3Hc/GSc5P0oHn4aO2uGuYyiose044wBQIm6F1dwfh5ARs8R8L2BHGfnQHv9PS4PjxrGLtFKxysm4qD3oCxboIIo7e1jhQHzAYUJ8wP8VYM56j1ktaal1BMrS2sAaK1OQDD6eUepY9yfQVR5zv7hrmaSaRtzyHJY0DcE4BPIPb5UBwOO4oJYzFvzEYHAH9XvQhNZN7AEgDOSDRo/wBA1SXRdVtNQgb/AJUgfBHcZ/8AGaGPVlhPbarZ2t5CEeORBKje2RUrI1zYxTBhMinIwcjvUEcugWkO4AMisMEA8N69u3yoERrOhWNxNYy6hapcxAeJHM+D8u9ASLStLlm/EWdpZyxy+Z3RQ3I5B44oJKTT4blFWSNBEOeBg5+goFYbOK1TKhIxnIbGMH50Cpcyo6AMrIcM2OD68Ggb6Lbbllu5Cd88hYBu6rztWglSNkfm4P70Gaat8Rba96w0vp3R2mhufxyi4kO3YyDOV9+f8VYMw+P0cP8Ax4JVuEKzW8bEr5tuMj0+lUZ7Y2X4288GK+towRlZXYqv9uKCQi6YmbT2u11GzaJWKnYxbBB49KCFnhubdiwL+U8OhP60ElpXUFxY6nDdXQF0qYbZKxwT9vWg3npb42aHcmC11C3uLSQjEkpIdQfr3xUondP6x6e6tv7e101hczRzrNtaFiFC/wAxPYHtUF+lj8bYCTgMG59cUC4OBQQOo2eoXepFTeL/AKYQN1sEwWx6Fu+DQLX2kw3Ok3Vjas9qJ48Exd1PHb9KCE6R0G86Ut7mK71KK4gklM7TyKRIBjtjtjj96AOreudJsrMJbXksk8jBCbVdzRjONxBHP/3QQ9x1jcWGkERWWqXdpMPCt7x4wfFOOWPbA+fAqwZr8TJ9Qbp2ymvEjsrSTEVvawyHz45Lv6E4wPqaoyl8g91OKDlYEc8mgKW5PH7UE80f8PPHl7gDFAZkUjawUIQG8w5ouknAUbl9Dg59KK1X4Z9XXFvo8mlRXax3KHfBG8Rk3qe6jHOc54+dMZO7jr/qK8vWtba4tYu38TwvDOMc8N60wPLbrW10PS7pnvrnUtbAKobgBUUk9jg8f/lMC2rydMdRSaRqWoLEbx4UefwxkL2BVvvn9Klgv3Tp0lPxFno6wJ4IVmEOMEHsf2xUD2e4ks7n+NGDaCMu8q8lCMcEfPNA6tW/EwrMybEYZUHnI9Cf/FAzu7G4LXTWs38SRNqhs4Bz3z9KCO6x07UNT6altNMvEs7xkC7nPlI9R2/egwbrDT+r+kupLCeK9nu3KBYGRy+QvdWFWCU6avtA6h1iKDqLRhpmpvkxz2p8JGb3z7k557VRX77TdHteuPBut401CAouyXGcdvmASaC069030brYaay/CwPFwWspRErfUN7UGZX2hWSiY6ZrMc0CvhUlBUk/UcGgiLq2mtG8F54yDwfDfIoGkrRsSZXwOB2zmgsvw06XHVfUcFvI22xQ753Ze4H8v3OBUo9a6XodjpltHbabDHZIhVsQqBuA9DxznFQTQUe5oDYGMnn7UEbpV3JqDyz+BJFbBtsXiDBf/qx6CgDXL42cSRwGM3UzBQrHGFzy32oG2i2kKTSI80lzMow0rqQoyew+3tQO59KtJJRI9rEzgg7igz3z/egZ6paJdGGwW4eBXy7pGeXUdwT6A5qwebPjJrcOr9TvbWZQ2Onr+Gi2nIJHcj7+vyqigOuRk+vtxQAqEk4BU0ABj7j96CzzKxYD0GeM0CQG3OMAjkfOgbSZwzE4PJyfWi6caTdzaffW95akrPFIrLg/tRHpGzs9C6t0W31FrO3Y43MrcbH9c/eloZ6v0JpWoKlrHHBbScSFEHYc5Pz71NDXUoJrK1g0dvw9qsspW3nXaN6KMrHnH5ieSfan0U3Rr1uidaRbiwk8BUSS6naQkjc3ZcHaRnn70wbja6lZX+nw3NvMksM+FXnPJ9DUCeoXj2hSG2t2km7op4U/f/FAvHaNePb3N0jRzRA7VD8Akc9u/FAvcxnawZQ3HYtjJoKfDFAdeub6Vo1dSULIBtTbgYOfU8jNWUVvrm5ih0m1urixhlsI5HJliOwR5/LkkHHJPamjHdQ0HWdemlutN0+YWBYtC0rHDhjwVz3zWgx1n4e9U6QE8XT5Zd//APR5se9XBXbjS9S0zAvbO5tyWKAOhG4/KpQ3ZHXO5JFK98qRUGhfCbph77Uvx91pv463wVjR0LR7s483796WjW7rTrXpHWrSW2YK7lmXTbaIFpCRjjHOOSeeBipaNLtXuIre3R43lnkGXYADZnnmoH6Dkbzn6UDGWe9a/hKG3jsW8riQMJS3svp86B6zgMQmDtGT8qDNb6XUpOoPx72tzOkjFYowOduDwDjj70Gg6Wsq2KNeAJKRuKk52fIn5DvVkENrvW+iaSAsl0txI2Asdud5Yk4A4pgzv4l9ST6JZSXbyyprWpw+FFa5G21gz5icfzH3pgwCSQlh688+tUEwjjngZ7UHAHuWAHpn+1AXj5frQWXOGBZcKSe3c/rQIyAtnI27eBj/ADQIMAuH8vHBB9DQwVpPLjOckEZ/ahi9/Czqj/S9VhtLm6aG1uZFUsT5VOfX5Gpg9GiNJArxsrxsv1BHypYGF7pljeG3kvLZSlqzNGGxhSRjP6GoG1yLUWiWc2nSPA4KJGItyYHYHHarop3wu0jWYNUvzriNBp8ErraRMANxJ/N7nA7ZqDUHgSQLvAbacjI7H3oOuIFuYzGS68jJRyp4+lA0m0yDwGjiTw3bzBwTuDe+TQVbV+mLmW1NtbSok9weZiC2zPLEZ+/FBM6Xo40/TYdL8Jr21G4vJcuCck55HbFWUKQ/h4tR/DTz2o8TK21qmMgKOf8AfpmrokljG1i5BHI57D5VNorut9Lab1LA638W6H8sboNrqQckq3pntV0RWsfD6K7SGC3vTFahQJY2iVmkI9d+Mimie0Hp2DQ7AQacio3JZgqjcT3zxk1KHGldPWtnqMupS5uNTmGGnk5KjGNqf0r8qgmkhVGcquGblm96BDUZZYLGVrdN8+MIvux7UGKTdXdbaRrFvbaxbWN4d58BmwCjHPORycLx2q4LNe9S9TdN6I13qkWmzSXdwBCGlO4hiMKAB2A9ag0WySQwpLLtMjDcQBhVz6CgoHxF17XbnUYunulgsUsu4TTvjIUAEhR+x4qwZwtkOi7651PXJobm4tohHbQhdgecjnaPZeOfeqMy1vWLvWNQlvb+ZpJpWyT2A9gB6Cgjy5AO4A5oAGRgBR39aA7Z8MkFtxPY0AbV9UGfpQWhj5TkBQGxuBoEGG1iQCyHvj1oELgSkK6oRnIBIxzRdJ28Q3+fHiAds96LoH3AF1wCfyijNbF8JviI1rbQ6Pq/iSopxFOx5Uf0n3qUbWQk8II2SRyDPuCDUHMpSIiJQSBwDwKAgTxApnEbyIQ+APyn0+9A5B3AgfmoEWgcb3VlMpGFYjt+negSs7zxH/C3RWO9UElM8SAHG5fl/agNdXcEbJAZ1WadvDQDJO7Gf7c0ED1dqWv2enzw6Rb24nZfJd3EwREHqxyMZHzOKBbT7H8PZWTK5uZ9o3Xm1SzEry5PsT7UFF+JnUezSZ9LttRs2km2m5KSFWXDZI491AyBzVwK6J8T7CRtPjee0tbaGAtdNISdoXgLEo5JJxyfSmC6aF1fo2vELZXDJOxwkMybHYe4HtUFiJWJd8rKqjuTQcZV8SNI0dy43BlGVA+ZoBniE0RU7tp77Tg5zQUv4hNrU2tdNWOhylPEnaS5UHGYlAzn5cn74oHGt6l0z07k6nJC123HhKPFlbPptGTj9q0Kx0XMvU+ty6vqQtpWlZo4LOdGDWsak8Aflycgk+v2qC8a1q8OnaXLPOz2kCIWkZ+CqjgYx6n0xTBkmp9Sabp0KdRyI5vJkaGw08MVKIDw8jA557896QY1q2o3eqXr3N7O8skjnlnzgn5e1UMCGV/MOM0BJFOVwfX0oFtgZTjkg8UBtpOeO1B3hg85FBZXVMM+VJAA2+h96BO4IMJ/p7qvuKBKacmOKB5CYYx5UzgDPfHzoGwD5OApJHAHrQEAk8NWdNpx+XdkA0AIGhkGWOQcgg8UGw/DP4kmwhi07WCTZqAiSbstGc4+pFKNvs5o7q2Sa3kWaJxkOp4NZDOHUh/qL2k8LW78eG7kbZv+0+/yoEZp7fUpX/BXpgvYZTCSRtO7vtwe/vQdY6jeyatPp91FEPw8aSNMoYbi2cADt6Z70DS60KW7luJdV1JniJzBtURG2b0KN7+/vQKSWUWnLLqN3cSLP4ex5Y8jxiPykr23+nzzigpXUPVOu6Vqmmf8UWttb9OXDqkjRnfI3H849uRkDOKC0axLFr+nLB0rrUMM0bqCIGGGX1AH09qsGc9UfBiTV9Vhu9Pu5oPGLNeG6bczN7jHvVEr058Gre3sLeDVrmOdo3LmSFNjEH+XdntQXO51XQOl5YrCKGWa8SMYS3tzMyLjjJHb9alDqz1S5ktJ7nVdLmSVDiOONfEMiE4Xy+h9xUDm11CaTxEOn3VmpHFxKFCr9s5GPmKAus6jb9M6RJf3c88yxpjcxL7uM5OO3HrQVTSupoOuYdZMTSpptriNFtXKXDA/Pjhs9h2xQOJ7Xpnpa2S91WK2swqjbGw3ysfcnuxrQsGmapYvpwvra1FtDL52Mi+Gx49sZoMb+LXV0t+jWl3OYLGTO2zjx4h2nyszHsG4/Sgxt5ZJmEsjl3PB3GgSlyXBxkDnNADseB2PsaA8SoXQyFgmQCV70B5R/EIjYmPJxnvigEKcYDfrQF2/X9aCwqC7l2zwfT/FAJJ5747E5oGTqZArKOfccftQHZWwmOD23Z4NAEkTEBmwR7g5AoG7KSSE5HyFAMTyQsMHa/cEcEc5oL58P+v7rppdryPNAXx+GfsQe7Z9D2/Wg3zSdX0fqzT08F433eYwscOpHt9PcVkQPW632mX9vfWdrbXiRgrIdu2eHIwGD57+nIoKdc/ELVdC0u5afp27SUtta5vJMMzk+UDjzYHtQWDpj4gxXmif/wAitXZkGZzFGW8MehZO+PmM0Fibr/poWQmF6xXA2xmFg59sKRk0C2nQP1KFvdb0vwIUJNtDKcsVP8zD0Jx2oJDTNA0vR5p7qzs44pJOXkUc/SgNfa/pNvbF5L2JgTsCo2WJzjGKA1jcNcxOF/m/I0zbt4+gxgenNA5s7CCxWWYQxpNLgyMiY3nt2oHajcuexAoEL1C1uyFkVHO1ixHb17/KgwT4rdXWep6oul2OpywaTYqVLxDyySDjaM9x6Z+tWDPdB1TW7Wa9sumpGlursqMQRlpXwd3BA4571RcdN0i41G7h/wBSmNxqdkwn1O6u5/4cAXlYgcnngE/pQNvih8S211obHRyYbWInfJG/Ex9MD2+tBmNzcTXTtJcSSSSHH5jngDAH0oEdxbg5A9wKAzPwO+fegSLfLJoFoR5fUfegXOcHGAT8qAM+XaBzQCCwGDuyPlQWJpV37Q5TIweM7u/f9qA9vNYpFML2KaR8YiaJgAh55PvQRvnYoqA+MThcDnPai4PKWQlH3K6tggjnPaiEmz4ZznBPfFAVWG0AZDH1z2HzoE7lhv8AJIHI/mGRQI8AEeuOMGgndN6pu7V4RJLKY4WDLhypXj0oNL6d+MMS4ttetDdQgDNwAPEwPRh2NZGkabrvTXVZiexvba5aI+ILWZQG3Y4IDdvtQScvTdjK8chtFjkQ+VlYgqPXGKA17daV07apJrV9CsZbELTgbu3YY78UEHf9evJ4K9P6JqGoiR1XxjCUjAJ5OT3NBM9YdSWPTenwy3t7bWbSuBunUthfUhRyT+1BA2vXnS/jGdeo9LnIHljeLwSCe5zgmgejrOz1S3kGhazoaXYGAJ5Sw3e38uaCsJfX02rPD1XfXtvcCXdBJGjLbOO+EK88Y7nIoLB1H1Bb6WkN1ddSQ29io/5MZEjzt7DGTjj2FXNGUfEH4wRaxCtnp2kwGGM7llvBvIOO4XOAe/fNMwZbqusalfLBHfzSvFH+SIgKo+igYqiwWfWV30rpp03p6exJnUPJexQnxuR+Ulu2PkKCrT6jcSiTxZnbxCWcFidxPJJ96BBDlQ35fbFAbahBHJIGO+KAIwg5YnB455oDkKcbW7UAOowSMjOORQcCVXPp+9AqDkHcDmgMNpHPcUBTuzQWCJXcFvKR2Y0CMiqjnz4UcH/6oG8gKluwxyAfSjQ6ylEJBJfv37/OiYQMjbjkZXOSP/FEELAEkNn1waBCXdjORnPoc80CZeTb5wQe2BQAm4y7jlhjvQcWO3cW4GB7ZpgGC5a3uUeNyGQ5GCR+45pgt+l/EzqLTgxj1O5Zc4CSOXCj70wOE+Jd/Pq0V7qVvb3bISAWUBhnuc+/2pgvkHx0soLaNIdKkEiqR5yDg47cYpgresfELSNc1n8VrFtAw2ZBiiywwcgeb14x2xTBYNA13ozUo/GOqWVizDc1nf6crIh/7wOf1rOURvVupdE6ncpFeakiSWsZdbjSLfw1Zs+VVyMHA75xWsFDHU0idTJdf65rT28YKpPvHjKp9Bk49qYK/rGpXF/qU9zPdyzyyMSJJAAx9ifnVlwNZ7vxYEh8GAEHO8DDH5H5U0IPK8jHxSzFQAMnOKgJkFwPT6UBlAII5z3zQCjnA5OKBXeuAT9KABMA208DtxQHRhzgUC3KjJx9z2oAP6/L3NAHC8Hg/XNAcNtGe4oEy5yeaCdLk7yx2qQCAO1AmXZSSexHbPNAhJuLJkgjvzRonuAJy2cd8UCbyOi453Dj7GiYTZyVPPl74oYLJIyq68EH3Gf3oYTDEjLbiP1FEDHOUOdgOfXIBFAm77j5Mnng/WgLI5UE8Eg9iO9AnuJbuQx5wOBQFRxuIbOc54PrQK28ws76F722EyI4d4HJUOPb35yKBm8oeQsi4BPbPb71RyYIHmPGRg00GRsbsHIPY0Bg52AEEseBUCQJyOPXtQDtcdvvjtQCQ/GBwKAuXU4PrQBvZE5IGeO1AffjBJ57UBvEO045HagFWAAzktQKIzBeMg0C5kz5mOG/WgMrDJJ7jmgEnIyOccYoA3cEMRj05oC7/wDeBQTduzEoNxwcZGaAJOWfPNAlISVOT60aIQfkj+amgJ3bnnigKeFGPQUCf/x0Smw4V8exogX/AOY3+/SgKxKxeU459KAgJOckntQJkkcgkGgAAFFz/XQEmJaY7jnk96BM9yPQelAf/wCX7UBv/jagGP8AKB6ZoDf00HMfO/0oAH5TQA/5moECSMDPFAvGASMjPP8AigVX+b60BW7/AHoHEJJD55oDd4snv70CsSjCcDmgVAAbgYoGYJ3nk9qBUAYHAoP/2Q==</binary></FictionBook> - +<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>foo<p>And nested without indentation:</p> +<p>foo</p>bar<p>Interpreted markdown in a table:</p>This is <emphasis>emphasized</emphasis>And this is <strong>strong</strong> +<p>Here’s a simple block:</p> +<p>foo</p> +<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>foo<p>This should just be an HTML comment:</p> +<p>Multiline:</p> +<p>Code block:</p> +<empty-line /> +<p> +<code><!-- Comment --></code> +</p> +<empty-line /> +<p>Just plain comment, with trailing spaces on the line:</p> +<p>Code:</p> +<empty-line /> +<p> +<code><hr /></code> +</p> +<empty-line /> +<p>Hr’s:</p> +<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>——————————</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>With embedded [brackets]<a l:href="#l13" type="note"> +<sup>[13]</sup> +</a>.</p> +<p>b<a l:href="#l14" type="note"> +<sup>[14]</sup> +</a> by itself should be a link.</p> +<p>Indented once<a l:href="#l15" type="note"> +<sup>[15]</sup> +</a>.</p> +<p>Indented twice<a l:href="#l16" type="note"> +<sup>[16]</sup> +</a>.</p> +<p>Indented thrice<a l:href="#l17" type="note"> +<sup>[17]</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="#l18" type="note"> +<sup>[18]</sup> +</a>.</p> +<p>Foo biz<a l:href="#l19" type="note"> +<sup>[19]</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="#l20" type="note"> +<sup>[20]</sup> +</a>.</p> +<p>Here’s a link with an amersand in the link text: AT&T<a l:href="#l21" type="note"> +<sup>[21]</sup> +</a>.</p> +<p>Here’s an inline link<a l:href="#l22" type="note"> +<sup>[22]</sup> +</a>.</p> +<p>Here’s an inline link in pointy braces<a l:href="#l23" type="note"> +<sup>[23]</sup> +</a>.</p> +</section> +<section> +<title> +<p>Autolinks</p> +</title> +<p>With an ampersand: http://example.com/?foo=1&bar=2<a l:href="#l24" type="note"> +<sup>[24]</sup> +</a> +</p> +<p>• In a list?</p> +<p>• http://example.com/<a l:href="#l25" type="note"> +<sup>[25]</sup> +</a> +</p> +<p>• It should.</p> +<p>An e-mail address: nobody@nowhere.net<a l:href="#l26" type="note"> +<sup>[26]</sup> +</a> +</p> +<cite> +<p>Blockquoted: http://example.com/<a l:href="#l27" type="note"> +<sup>[27]</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="#n28" type="note"> +<sup>[28]</sup> +</a> and another.<a l:href="#n29" type="note"> +<sup>[29]</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="#n30" type="note"> +<sup>[30]</sup> +</a> +</p> +<cite> +<p>Notes can go in quotes.<a l:href="#n31" type="note"> +<sup>[31]</sup> +</a> +</p> +</cite> +<p> 1. And in list items.<a l:href="#n32" type="note"> +<sup>[32]</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>Title with "quotes" inside: <code>/url/</code> +</p> +</section> +<section id="l19"> +<title> +<p>19</p> +</title> +<p>Title with "quote" inside: <code>/url/</code> +</p> +</section> +<section id="l20"> +<title> +<p>20</p> +</title> +<p> +<code>http://example.com/?foo=1&bar=2</code> +</p> +</section> +<section id="l21"> +<title> +<p>21</p> +</title> +<p>AT&T: <code>http://att.com/</code> +</p> +</section> +<section id="l22"> +<title> +<p>22</p> +</title> +<p> +<code>/script?foo=1&bar=2</code> +</p> +</section> +<section id="l23"> +<title> +<p>23</p> +</title> +<p> +<code>/script?foo=1&bar=2</code> +</p> +</section> +<section id="l24"> +<title> +<p>24</p> +</title> +<p> +<code>http://example.com/?foo=1&bar=2</code> +</p> +</section> +<section id="l25"> +<title> +<p>25</p> +</title> +<p> +<code>http://example.com/</code> +</p> +</section> +<section id="l26"> +<title> +<p>26</p> +</title> +<p> +<code>mailto:nobody@nowhere.net</code> +</p> +</section> +<section id="l27"> +<title> +<p>27</p> +</title> +<p> +<code>http://example.com/</code> +</p> +</section> +<section id="n28"> +<title> +<p>28</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="n29"> +<title> +<p>29</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="n30"> +<title> +<p>30</p> +</title> +<p>This is <emphasis>easier</emphasis> to type. Inline notes may contain links<a l:href="#l30" type="note"> +<sup>[30]</sup> +</a> and <code>]</code> verbatim characters, as well as [bracketed text].</p> +</section> +<section id="n31"> +<title> +<p>31</p> +</title> +<p>In quote.</p> +</section> +<section id="n32"> +<title> +<p>32</p> +</title> +<p>In list.</p> +</section> +</body> +</FictionBook> diff --git a/test/writer.haddock b/test/writer.haddock index 0772331e3..7f783abd1 100644 --- a/test/writer.haddock +++ b/test/writer.haddock @@ -560,10 +560,6 @@ Just a </url/ URL>. Foo </url/ bar>. -Foo </url/ bar>. - -Foo </url/ bar>. - With </url/ embedded [brackets]>. </url/ b> by itself should be a link. diff --git a/test/writer.html4 b/test/writer.html4 index bac16b14c..89cf07685 100644 --- a/test/writer.html4 +++ b/test/writer.html4 @@ -486,8 +486,6 @@ Blah <p><a href="">Empty</a>.</p> <h2 id="reference">Reference</h2> <p>Foo <a href="/url/">bar</a>.</p> -<p>Foo <a href="/url/">bar</a>.</p> -<p>Foo <a href="/url/">bar</a>.</p> <p>With <a href="/url/">embedded [brackets]</a>.</p> <p><a href="/url/">b</a> by itself should be a link.</p> <p>Indented <a href="/url">once</a>.</p> diff --git a/test/writer.html5 b/test/writer.html5 index ee921766c..6762f8198 100644 --- a/test/writer.html5 +++ b/test/writer.html5 @@ -489,8 +489,6 @@ Blah <p><a href="">Empty</a>.</p> <h2 id="reference">Reference</h2> <p>Foo <a href="/url/">bar</a>.</p> -<p>Foo <a href="/url/">bar</a>.</p> -<p>Foo <a href="/url/">bar</a>.</p> <p>With <a href="/url/">embedded [brackets]</a>.</p> <p><a href="/url/">b</a> by itself should be a link.</p> <p>Indented <a href="/url">once</a>.</p> diff --git a/test/writer.icml b/test/writer.icml index b498f568b..6e070e264 100644 --- a/test/writer.icml +++ b/test/writer.icml @@ -2566,37 +2566,9 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <Br /> <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Content>Foo </Content> - </CharacterStyleRange> - <HyperlinkTextSource Self="htss-14" Name="" Hidden="false"> - <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> - <Content>bar</Content> - </CharacterStyleRange> - </HyperlinkTextSource> - <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Content>.</Content> - </CharacterStyleRange> -</ParagraphStyleRange> -<Br /> -<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> - <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Content>Foo </Content> - </CharacterStyleRange> - <HyperlinkTextSource Self="htss-15" Name="" Hidden="false"> - <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> - <Content>bar</Content> - </CharacterStyleRange> - </HyperlinkTextSource> - <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Content>.</Content> - </CharacterStyleRange> -</ParagraphStyleRange> -<Br /> -<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> - <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>With </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-16" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-14" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>embedded [brackets]</Content> </CharacterStyleRange> @@ -2607,7 +2579,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> </ParagraphStyleRange> <Br /> <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Paragraph"> - <HyperlinkTextSource Self="htss-17" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-15" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>b</Content> </CharacterStyleRange> @@ -2621,7 +2593,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Indented </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-18" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-16" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>once</Content> </CharacterStyleRange> @@ -2635,7 +2607,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Indented </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-19" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-17" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>twice</Content> </CharacterStyleRange> @@ -2649,7 +2621,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Indented </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-20" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-18" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>thrice</Content> </CharacterStyleRange> @@ -2675,7 +2647,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Foo </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-21" Name="Title with "quotes" inside" Hidden="false"> + <HyperlinkTextSource Self="htss-19" Name="Title with "quotes" inside" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>bar</Content> </CharacterStyleRange> @@ -2689,7 +2661,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Foo </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-22" Name="Title with "quote" inside" Hidden="false"> + <HyperlinkTextSource Self="htss-20" Name="Title with "quote" inside" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>biz</Content> </CharacterStyleRange> @@ -2709,7 +2681,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Here’s a </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-23" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-21" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>link with an ampersand in the URL</Content> </CharacterStyleRange> @@ -2723,7 +2695,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Here’s a link with an amersand in the link text: </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-24" Name="AT&T" Hidden="false"> + <HyperlinkTextSource Self="htss-22" Name="AT&T" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>AT&T</Content> </CharacterStyleRange> @@ -2737,7 +2709,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Here’s an </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-25" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-23" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>inline link</Content> </CharacterStyleRange> @@ -2751,7 +2723,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Here’s an </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-26" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-24" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>inline link in pointy braces</Content> </CharacterStyleRange> @@ -2771,7 +2743,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>With an ampersand: </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-27" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-25" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>http://example.com/?foo=1&bar=2</Content> </CharacterStyleRange> @@ -2785,7 +2757,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> </ParagraphStyleRange> <Br /> <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/BulList"> - <HyperlinkTextSource Self="htss-28" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-26" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>http://example.com/</Content> </CharacterStyleRange> @@ -2802,7 +2774,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>An e-mail address: </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-29" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-27" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>nobody@nowhere.net</Content> </CharacterStyleRange> @@ -2813,7 +2785,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content>Blockquoted: </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-30" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-28" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>http://example.com/</Content> </CharacterStyleRange> @@ -2861,20 +2833,20 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <Br /> <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/Figure"> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1.00000 0 0 1.00000 75.00000 -75.00000"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 75 -75"> <Properties> <PathGeometry> <GeometryPathType PathOpen="false"> <PathPointArray> - <PathPointType Anchor="-75.00000 -75.00000" LeftDirection="-75.00000 -75.00000" RightDirection="-75.00000 -75.00000" /> - <PathPointType Anchor="-75.00000 75.00000" LeftDirection="-75.00000 75.00000" RightDirection="-75.00000 75.00000" /> - <PathPointType Anchor="75.00000 75.00000" LeftDirection="75.00000 75.00000" RightDirection="75.00000 75.00000" /> - <PathPointType Anchor="75.00000 -75.00000" LeftDirection="75.00000 -75.00000" RightDirection="75.00000 -75.00000" /> + <PathPointType Anchor="-75 -75" LeftDirection="-75 -75" RightDirection="-75 -75" /> + <PathPointType Anchor="-75 75" LeftDirection="-75 75" RightDirection="-75 75" /> + <PathPointType Anchor="75 75" LeftDirection="75 75" RightDirection="75 75" /> + <PathPointType Anchor="75 -75" LeftDirection="75 -75" RightDirection="75 -75" /> </PathPointArray> </GeometryPathType> </PathGeometry> </Properties> - <Image Self="ue6" ItemTransform="1.00000 0 0 1.00000 -75.00000 -75.00000"> + <Image Self="ue6" ItemTransform="1 0 0 1 -75 -75"> <Properties> <Profile type="string"> $ID/Embedded @@ -2897,20 +2869,20 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <Content>Here is a movie </Content> </CharacterStyleRange> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> - <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1.00000 0 0 1.00000 10.00000 -11.00000"> + <Rectangle Self="uec" StrokeWeight="0" ItemTransform="1 0 0 1 10 -11"> <Properties> <PathGeometry> <GeometryPathType PathOpen="false"> <PathPointArray> - <PathPointType Anchor="-10.00000 -11.00000" LeftDirection="-10.00000 -11.00000" RightDirection="-10.00000 -11.00000" /> - <PathPointType Anchor="-10.00000 11.00000" LeftDirection="-10.00000 11.00000" RightDirection="-10.00000 11.00000" /> - <PathPointType Anchor="10.00000 11.00000" LeftDirection="10.00000 11.00000" RightDirection="10.00000 11.00000" /> - <PathPointType Anchor="10.00000 -11.00000" LeftDirection="10.00000 -11.00000" RightDirection="10.00000 -11.00000" /> + <PathPointType Anchor="-10 -11" LeftDirection="-10 -11" RightDirection="-10 -11" /> + <PathPointType Anchor="-10 11" LeftDirection="-10 11" RightDirection="-10 11" /> + <PathPointType Anchor="10 11" LeftDirection="10 11" RightDirection="10 11" /> + <PathPointType Anchor="10 -11" LeftDirection="10 -11" RightDirection="10 -11" /> </PathPointArray> </GeometryPathType> </PathGeometry> </Properties> - <Image Self="ue6" ItemTransform="1.00000 0 0 1.00000 -10.00000 -11.00000"> + <Image Self="ue6" ItemTransform="1 0 0 1 -10 -11"> <Properties> <Profile type="string"> $ID/Embedded @@ -3025,7 +2997,7 @@ These should not be escaped: \$ \\ \> \[ \{</Content> <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle"> <Content> to type. Inline notes may contain </Content> </CharacterStyleRange> - <HyperlinkTextSource Self="htss-31" Name="" Hidden="false"> + <HyperlinkTextSource Self="htss-29" Name="" Hidden="false"> <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/Link"> <Content>links</Content> </CharacterStyleRange> @@ -3098,118 +3070,104 @@ These should not be escaped: \$ \\ \> \[ \{</Content> </Story> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//google.com" Name="link" DestinationURL="http://google.com" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-31" Name="http://google.com" Source="htss-31" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-29" Name="http://google.com" Source="htss-29" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//google.com</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//example.com/" Name="link" DestinationURL="http://example.com/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-30" Name="http://example.com/" Source="htss-30" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-28" Name="http://example.com/" Source="htss-28" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//example.com/</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/mailto%3anobody@nowhere.net" Name="link" DestinationURL="mailto:nobody@nowhere.net" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-29" Name="mailto:nobody@nowhere.net" Source="htss-29" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-27" Name="mailto:nobody@nowhere.net" Source="htss-27" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/mailto%3anobody@nowhere.net</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//example.com/" Name="link" DestinationURL="http://example.com/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-28" Name="http://example.com/" Source="htss-28" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-26" Name="http://example.com/" Source="htss-26" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//example.com/</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2" Name="link" DestinationURL="http://example.com/?foo=1&bar=2" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-27" Name="http://example.com/?foo=1&bar=2" Source="htss-27" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-25" Name="http://example.com/?foo=1&bar=2" Source="htss-25" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//script?foo=1&bar=2" Name="link" DestinationURL="/script?foo=1&bar=2" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-26" Name="/script?foo=1&bar=2" Source="htss-26" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-24" Name="/script?foo=1&bar=2" Source="htss-24" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//script?foo=1&bar=2</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//script?foo=1&bar=2" Name="link" DestinationURL="/script?foo=1&bar=2" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-25" Name="/script?foo=1&bar=2" Source="htss-25" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-23" Name="/script?foo=1&bar=2" Source="htss-23" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//script?foo=1&bar=2</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//att.com/" Name="link" DestinationURL="http://att.com/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-24" Name="http://att.com/" Source="htss-24" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-22" Name="http://att.com/" Source="htss-22" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//att.com/</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2" Name="link" DestinationURL="http://example.com/?foo=1&bar=2" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-23" Name="http://example.com/?foo=1&bar=2" Source="htss-23" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-21" Name="http://example.com/?foo=1&bar=2" Source="htss-21" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination/http%3a//example.com/?foo=1&bar=2</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url/" Name="link" DestinationURL="/url/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-22" Name="/url/" Source="htss-22" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-20" Name="/url/" Source="htss-20" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//url/</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url/" Name="link" DestinationURL="/url/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-21" Name="/url/" Source="htss-21" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-19" Name="/url/" Source="htss-19" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//url/</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url" Name="link" DestinationURL="/url" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-20" Name="/url" Source="htss-20" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-18" Name="/url" Source="htss-18" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//url</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url" Name="link" DestinationURL="/url" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-19" Name="/url" Source="htss-19" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-17" Name="/url" Source="htss-17" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//url</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url" Name="link" DestinationURL="/url" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-18" Name="/url" Source="htss-18" Visible="true" DestinationUniqueKey="1"> + <Hyperlink Self="uf-16" Name="/url" Source="htss-16" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> <Destination type="object">HyperlinkURLDestination//url</Destination> </Properties> </Hyperlink> <HyperlinkURLDestination Self="HyperlinkURLDestination//url/" Name="link" DestinationURL="/url/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-17" Name="/url/" Source="htss-17" Visible="true" DestinationUniqueKey="1"> - <Properties> - <BorderColor type="enumeration">Black</BorderColor> - <Destination type="object">HyperlinkURLDestination//url/</Destination> - </Properties> - </Hyperlink> - <HyperlinkURLDestination Self="HyperlinkURLDestination//url/" Name="link" DestinationURL="/url/" DestinationUniqueKey="1" /> - <Hyperlink Self="uf-16" Name="/url/" Source="htss-16" Visible="true" DestinationUniqueKey="1"> - <Properties> - <BorderColor type="enumeration">Black</BorderColor> - <Destination type="object">HyperlinkURLDestination//url/</Destination> - </Properties> - </Hyperlink> - <HyperlinkURLDestination Self="HyperlinkURLDestination//url/" Name="link" DestinationURL="/url/" DestinationUniqueKey="1" /> <Hyperlink Self="uf-15" Name="/url/" Source="htss-15" Visible="true" DestinationUniqueKey="1"> <Properties> <BorderColor type="enumeration">Black</BorderColor> diff --git a/test/writer.jats b/test/writer.jats index 07fe24d73..3cb5050c2 100644 --- a/test/writer.jats +++ b/test/writer.jats @@ -1267,12 +1267,6 @@ These should not be escaped: \$ \\ \> \[ \{</preformat> Foo <ext-link ext-link-type="uri" xlink:href="/url/">bar</ext-link>. </p> <p> - Foo <ext-link ext-link-type="uri" xlink:href="/url/">bar</ext-link>. - </p> - <p> - Foo <ext-link ext-link-type="uri" xlink:href="/url/">bar</ext-link>. - </p> - <p> With <ext-link ext-link-type="uri" xlink:href="/url/">embedded [brackets]</ext-link>. </p> diff --git a/test/writer.latex b/test/writer.latex index f88621a28..207e30569 100644 --- a/test/writer.latex +++ b/test/writer.latex @@ -17,6 +17,12 @@ \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} \PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref \usepackage{fancyvrb} \usepackage[unicode=true]{hyperref} @@ -39,12 +45,6 @@ \usepackage[normalem]{ulem} % avoid problems with \sout in headers with hyperref: \pdfstringdefDisableCommands{\renewcommand{\sout}{}} -\IfFileExists{parskip.sty}{% -\usepackage{parskip} -}{% else -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -} \setlength{\emergencystretch}{3em} % prevent overfull lines \providecommand{\tightlist}{% \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} @@ -877,10 +877,6 @@ Just a \href{/url/}{URL}. Foo \href{/url/}{bar}. -Foo \href{/url/}{bar}. - -Foo \href{/url/}{bar}. - With \href{/url/}{embedded {[}brackets{]}}. \href{/url/}{b} by itself should be a link. diff --git a/test/writer.man b/test/writer.man index 907fb4878..f6d0deb92 100644 --- a/test/writer.man +++ b/test/writer.man @@ -677,10 +677,6 @@ Empty (). .PP Foo bar (/url/). .PP -Foo bar (/url/). -.PP -Foo bar (/url/). -.PP With embedded [brackets] (/url/). .PP b (/url/) by itself should be a link. diff --git a/test/writer.markdown b/test/writer.markdown index 3fe0f4b3e..d41030785 100644 --- a/test/writer.markdown +++ b/test/writer.markdown @@ -647,10 +647,6 @@ Reference Foo [bar](/url/). -Foo [bar](/url/). - -Foo [bar](/url/). - With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. diff --git a/test/writer.mediawiki b/test/writer.mediawiki index a0dc15fae..968eef388 100644 --- a/test/writer.mediawiki +++ b/test/writer.mediawiki @@ -571,10 +571,6 @@ Just a [[url/|URL]]. Foo [[url/|bar]]. -Foo [[url/|bar]]. - -Foo [[url/|bar]]. - With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. diff --git a/test/writer.ms b/test/writer.ms index 617ccc752..7e079c55d 100644 --- a/test/writer.ms +++ b/test/writer.ms @@ -835,16 +835,6 @@ Foo \c -- "bar" \&. .PP -Foo \c -.pdfhref W -D "/url/" -A "\c" \ - -- "bar" -\&. -.PP -Foo \c -.pdfhref W -D "/url/" -A "\c" \ - -- "bar" -\&. -.PP With \c .pdfhref W -D "/url/" -A "\c" \ -- "embedded [brackets]" diff --git a/test/writer.muse b/test/writer.muse index c19cb8ab2..41d1c9a5b 100644 --- a/test/writer.muse +++ b/test/writer.muse @@ -9,47 +9,30 @@ markdown test suite. * Headers -#headers - ** Level 2 with an [[/url][embedded link]] -#level-2-with-an-embedded-link - *** Level 3 with <em>emphasis</em> -#level-3-with-emphasis - **** Level 4 -#level-4 - ***** Level 5 -#level-5 - * Level 1 -#level-1 - ** Level 2 with <em>emphasis</em> -#level-2-with-emphasis - *** Level 3 -#level-3 with no blank line ** Level 2 -#level-2 with no blank line ---- * Paragraphs -#paragraphs Here’s a regular paragraph. In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. @@ -65,7 +48,6 @@ here. * Block Quotes -#block-quotes E-mail style: <quote> @@ -105,7 +87,6 @@ And a following paragraph. * Code Blocks -#code-blocks Code: <example> @@ -130,11 +111,8 @@ These should not be escaped: \$ \\ \> \[ \{ * Lists -#lists - ** Unordered -#unordered Asterisks tight: - asterisk 1 @@ -173,7 +151,6 @@ Minuses loose: ** Ordered -#ordered Tight: 1. First @@ -208,7 +185,6 @@ Multiple paragraphs: ** Nested -#nested - Tab - Tab - Tab @@ -234,7 +210,6 @@ Same thing but with paragraphs: ** Tabs and spaces -#tabs-and-spaces - this is a list item indented with tabs - this is a list item indented with spaces @@ -243,7 +218,6 @@ Same thing but with paragraphs: ** Fancy list markers -#fancy-list-markers 2. begins with 2 3. and now 3 @@ -277,7 +251,6 @@ B. Williams * Definition Lists -#definition-lists Tight using spaces: apple :: red fruit @@ -339,7 +312,6 @@ Blank line after term, indented marker, alternate markers: * HTML Blocks -#html-blocks Simple block on one line: fooAnd nested without indentation: @@ -489,7 +461,6 @@ Hr’s: * Inline Markup -#inline-markup This is <em>emphasized</em>, and so <em>is this</em>. This is <strong>strong</strong>, and so <strong>is this</strong>. @@ -521,7 +492,6 @@ spaces: a^b c^d, a~b c~d. * Smart quotes, ellipses, dashes -#smart-quotes-ellipses-dashes "Hello," said the spider. "'Shelob' is my name." 'A', 'B', and 'C' are letters. @@ -543,7 +513,6 @@ Ellipses…and…and…. * LaTeX -#latex - <literal style="tex">\cite[22-23]{smith.1899}</literal> - 2 + 2 <verbatim>=</verbatim> 4 - <em>x</em> ∈ <em>y</em> @@ -578,7 +547,6 @@ Cat & 1 \\ \hline * Special Characters -#special-characters Here is some unicode: - I hat: Î @@ -633,11 +601,8 @@ Minus: - * Links -#links - ** Explicit -#explicit Just a [[/url/][URL]]. [[/url/][URL and title]]. @@ -658,11 +623,6 @@ Just a [[/url/][URL]]. ** Reference -#reference -Foo [[/url/][bar]]. - -Foo [[/url/][bar]]. - Foo [[/url/][bar]]. With [[/url/][embedded <verbatim>[brackets]</verbatim>]]. @@ -687,7 +647,6 @@ Foo [[/url/][biz]]. ** With ampersands -#with-ampersands Here’s a [[http://example.com/?foo=1&bar=2][link with an ampersand in the URL]]. @@ -699,7 +658,6 @@ Here’s an [[/script?foo=1&bar=2][inline link in pointy braces]]. ** Autolinks -#autolinks With an ampersand: [[http://example.com/?foo=1&bar=2]] - In a list? @@ -723,7 +681,6 @@ or here: <http://example.com/> * Images -#images From "Voyage dans la Lune" by Georges Melies (1902): [[lalune.jpg][Voyage dans la Lune]] @@ -734,7 +691,6 @@ Here is a movie [[movie.jpg][movie]] icon. * Footnotes -#footnotes Here is a footnote reference,[1] and another.[2] This should <em>not</em> be a footnote reference, because it contains a <verbatim>space.[^my</verbatim> <verbatim>note]</verbatim> Here is an inline note.[3] diff --git a/test/writer.native b/test/writer.native index fa234dfc2..0587bddb8 100644 --- a/test/writer.native +++ b/test/writer.native @@ -369,8 +369,6 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa ,Para [Link ("",[],[]) [Str "Empty"] ("",""),Str "."] ,Header 2 ("reference",[],[]) [Str "Reference"] ,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] -,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] -,Para [Str "Foo",Space,Link ("",[],[]) [Str "bar"] ("/url/",""),Str "."] ,Para [Str "With",Space,Link ("",[],[]) [Str "embedded",Space,Str "[brackets]"] ("/url/",""),Str "."] ,Para [Link ("",[],[]) [Str "b"] ("/url/",""),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."] ,Para [Str "Indented",Space,Link ("",[],[]) [Str "once"] ("/url",""),Str "."] diff --git a/test/writer.opendocument b/test/writer.opendocument index 86d88ee27..77c79d13c 100644 --- a/test/writer.opendocument +++ b/test/writer.opendocument @@ -1434,10 +1434,6 @@ link</text:span></text:a></text:p> <text:h text:style-name="Heading_20_2" text:outline-level="2">Reference</text:h> <text:p text:style-name="First_20_paragraph">Foo <text:a xlink:type="simple" xlink:href="/url/" office:name=""><text:span text:style-name="Definition">bar</text:span></text:a>.</text:p> -<text:p text:style-name="Text_20_body">Foo -<text:a xlink:type="simple" xlink:href="/url/" office:name=""><text:span text:style-name="Definition">bar</text:span></text:a>.</text:p> -<text:p text:style-name="Text_20_body">Foo -<text:a xlink:type="simple" xlink:href="/url/" office:name=""><text:span text:style-name="Definition">bar</text:span></text:a>.</text:p> <text:p text:style-name="Text_20_body">With <text:a xlink:type="simple" xlink:href="/url/" office:name=""><text:span text:style-name="Definition">embedded [brackets]</text:span></text:a>.</text:p> diff --git a/test/writer.opml b/test/writer.opml index 51b0cb2d5..4e67652d2 100644 --- a/test/writer.opml +++ b/test/writer.opml @@ -57,7 +57,7 @@ <outline text="Links"> <outline text="Explicit" _note="Just a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [URL and title](/url/ "title with "quotes" in it") [URL and title](/url/ "title with single quotes") [with\_underscore](/url/with_underscore) [Email link](mailto:nobody@nowhere.net) [Empty]()."> </outline> - <outline text="Reference" _note="Foo [bar](/url/). Foo [bar](/url/). Foo [bar](/url/). With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should \[not\]\[\] be a link. [not]: /url Foo [bar](/url/ "Title with "quotes" inside"). Foo [biz](/url/ "Title with "quote" inside")."> + <outline text="Reference" _note="Foo [bar](/url/). With [embedded \[brackets\]](/url/). [b](/url/) by itself should be a link. Indented [once](/url). Indented [twice](/url). Indented [thrice](/url). This should \[not\]\[\] be a link. [not]: /url Foo [bar](/url/ "Title with "quotes" inside"). Foo [biz](/url/ "Title with "quote" inside")."> </outline> <outline text="With ampersands" _note="Here’s a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). Here’s a link with an amersand in the link text: [AT&T](http://att.com/ "AT&T"). Here’s an [inline link](/script?foo=1&bar=2). Here’s an [inline link in pointy braces](/script?foo=1&bar=2)."> </outline> diff --git a/test/writer.org b/test/writer.org index 96db87449..1ae0ca8f3 100644 --- a/test/writer.org +++ b/test/writer.org @@ -737,10 +737,6 @@ Just a [[/url/][URL]]. Foo [[/url/][bar]]. -Foo [[/url/][bar]]. - -Foo [[/url/][bar]]. - With [[/url/][embedded [brackets]]]. [[/url/][b]] by itself should be a link. diff --git a/test/writer.plain b/test/writer.plain index 175efb608..031c4a3e6 100644 --- a/test/writer.plain +++ b/test/writer.plain @@ -594,10 +594,6 @@ Reference Foo bar. -Foo bar. - -Foo bar. - With embedded [brackets]. b by itself should be a link. diff --git a/test/writer.rst b/test/writer.rst index 1aeeacacb..e81e79f3f 100644 --- a/test/writer.rst +++ b/test/writer.rst @@ -75,6 +75,8 @@ E-mail style: This is a block quote. It is pretty short. +.. + Code in a block quote: :: @@ -92,6 +94,8 @@ E-mail style: nested + .. + nested This should not be a block quote: 2 > 1. @@ -342,6 +346,8 @@ Multiple blocks with italics: { orange code block } + .. + orange block quote Multiple definitions, tight: @@ -777,10 +783,6 @@ Reference Foo `bar </url/>`__. -Foo `bar </url/>`__. - -Foo `bar </url/>`__. - With `embedded [brackets] </url/>`__. `b </url/>`__ by itself should be a link. diff --git a/test/writer.rtf b/test/writer.rtf index a79ae6fb5..c67c67a83 100644 --- a/test/writer.rtf +++ b/test/writer.rtf @@ -350,14 +350,6 @@ Empty bar }}} .\par} -{\pard \ql \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul -bar -}}} -.\par} -{\pard \ql \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul -bar -}}} -.\par} {\pard \ql \f0 \sa180 \li0 \fi0 With {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul embedded [brackets] }}} diff --git a/test/writer.tei b/test/writer.tei index 986240c86..ecbe92e33 100644 --- a/test/writer.tei +++ b/test/writer.tei @@ -754,8 +754,6 @@ These should not be escaped: \$ \\ \> \[ \{ <div type="level2" id="reference"> <head>Reference</head> <p>Foo <ref target="/url/">bar</ref>.</p> - <p>Foo <ref target="/url/">bar</ref>.</p> - <p>Foo <ref target="/url/">bar</ref>.</p> <p>With <ref target="/url/">embedded [brackets]</ref>.</p> <p><ref target="/url/">b</ref> by itself should be a link.</p> <p>Indented <ref target="/url">once</ref>.</p> diff --git a/test/writer.texinfo b/test/writer.texinfo index ca87da1a9..f5727d96d 100644 --- a/test/writer.texinfo +++ b/test/writer.texinfo @@ -939,10 +939,6 @@ Just a @uref{/url/,URL}. @anchor{#reference} Foo @uref{/url/,bar}. -Foo @uref{/url/,bar}. - -Foo @uref{/url/,bar}. - With @uref{/url/,embedded [brackets]}. @uref{/url/,b} by itself should be a link. diff --git a/test/writer.textile b/test/writer.textile index 293418ed5..d19b698f9 100644 --- a/test/writer.textile +++ b/test/writer.textile @@ -623,10 +623,6 @@ h2(#reference). Reference Foo "bar":/url/. -Foo "bar":/url/. - -Foo "bar":/url/. - With "embedded [brackets]":/url/. "b":/url/ by itself should be a link. diff --git a/test/writer.zimwiki b/test/writer.zimwiki index 7a15bad9d..91f018b52 100644 --- a/test/writer.zimwiki +++ b/test/writer.zimwiki @@ -538,10 +538,6 @@ Just a [[url/|URL]]. Foo [[url/|bar]]. -Foo [[url/|bar]]. - -Foo [[url/|bar]]. - With [[url/|embedded [brackets]]]. [[url/|b]] by itself should be a link. diff --git a/test/writers-lang-and-dir.context b/test/writers-lang-and-dir.context index 66dab9ead..250ee8c59 100644 --- a/test/writers-lang-and-dir.context +++ b/test/writers-lang-and-dir.context @@ -4,19 +4,28 @@ style=, color=, contrastcolor=] + % make chapter, section bookmarks visible when opening document \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] \setupinteractionscreen[option=bookmark] \setuptagging[state=start] + % use microtypography \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] \setupalign[hz,hanging] \setupitaliccorrection[global, always] + \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted -\usemodule[simplefonts] -\setmainfontfallback[DejaVu Serif][range={greekandcoptic, greekextended}, force=yes, rscale=auto] + +\definefallbackfamily[mainface][rm][DejaVu Serif][preset=range:greek, force=yes] +\definefontfamily[mainface][rm][Latin Modern Roman] +\definefontfamily[mainface][mm][Latin Modern Math] +\definefontfamily[mainface][ss][Latin Modern Sans] +\definefontfamily[mainface][tt][Latin Modern Typewriter][features=none] +\setupbodyfont[mainface] + \setupwhitespace[medium] \setuphead[chapter] [style=\tfd,header=empty] diff --git a/test/writers-lang-and-dir.latex b/test/writers-lang-and-dir.latex index cba3dd96b..0a7832a91 100644 --- a/test/writers-lang-and-dir.latex +++ b/test/writers-lang-and-dir.latex @@ -17,12 +17,31 @@ \usepackage[]{microtype} \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts }{} +\IfFileExists{parskip.sty}{% +\usepackage{parskip} +}{% else +\setlength{\parindent}{0pt} +\setlength{\parskip}{6pt plus 2pt minus 1pt} +} \PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref \usepackage[unicode=true]{hyperref} \hypersetup{ pdfborder={0 0 0}, breaklinks=true} \urlstyle{same} % don't use monospace font for urls +\setlength{\emergencystretch}{3em} % prevent overfull lines +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\setcounter{secnumdepth}{0} +% Redefines (sub)paragraphs to behave more like sections +\ifx\paragraph\undefined\else +\let\oldparagraph\paragraph +\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}} +\fi +\ifx\subparagraph\undefined\else +\let\oldsubparagraph\subparagraph +\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}} +\fi \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex \usepackage[shorthands=off,ngerman,british,nswissgerman,spanish,french,main=english]{babel} \newcommand{\textgerman}[2][]{\foreignlanguage{ngerman}{#2}} @@ -35,6 +54,7 @@ \newcommand{\textfrench}[2][]{\foreignlanguage{french}{#2}} \newenvironment{french}[2][]{\begin{otherlanguage}{french}}{\end{otherlanguage}} \else + % load polyglossia as late as possible as it *could* call bidi if RTL lang (e.g. Hebrew or Arabic) \usepackage{polyglossia} \setmainlanguage[]{english} \setotherlanguage[]{german} @@ -43,25 +63,6 @@ \setotherlanguage[]{spanish} \setotherlanguage[]{french} \fi -\IfFileExists{parskip.sty}{% -\usepackage{parskip} -}{% else -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -} -\setlength{\emergencystretch}{3em} % prevent overfull lines -\providecommand{\tightlist}{% - \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} -\setcounter{secnumdepth}{0} -% Redefines (sub)paragraphs to behave more like sections -\ifx\paragraph\undefined\else -\let\oldparagraph\paragraph -\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}} -\fi -\ifx\subparagraph\undefined\else -\let\oldsubparagraph\subparagraph -\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}} -\fi \ifxetex % load bidi as late as possible as it modifies e.g. graphicx \usepackage{bidi} diff --git a/trypandoc/trypandoc.hs b/trypandoc/trypandoc.hs index 2fcfe35e7..d8652079a 100644 --- a/trypandoc/trypandoc.hs +++ b/trypandoc/trypandoc.hs @@ -8,6 +8,9 @@ import Network.HTTP.Types.Status (status200) import Network.HTTP.Types.Header (hContentType) import Network.HTTP.Types.URI (queryToQueryText) import Text.Pandoc +import Text.Pandoc.Highlighting (pygments) +import Text.Pandoc.Readers (getReader, Reader(..)) +import Text.Pandoc.Writers (getWriter, Writer(..)) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Shared (tabFilter) import Data.Aeson @@ -25,12 +28,16 @@ app req respond = do text <- getParam "text" >>= checkLength . fromMaybe T.empty fromFormat <- fromMaybe "" <$> getParam "from" toFormat <- fromMaybe "" <$> getParam "to" - reader <- maybe (error $ "could not find reader for " ++ T.unpack fromFormat) return - $ lookup fromFormat fromFormats - let writer = maybe (error $ "could not find writer for " ++ T.unpack toFormat) id - $ lookup toFormat toFormats - let result = case reader $ tabFilter 4 $ T.unpack text of - Right doc -> T.pack $ writer doc + let reader = case getReader (T.unpack fromFormat) of + Right (TextReader r) -> r readerOpts + _ -> error $ "could not find reader for " + ++ T.unpack fromFormat + let writer = case getWriter (T.unpack toFormat) of + Right (TextWriter w) -> w writerOpts + _ -> error $ "could not find writer for " ++ + T.unpack toFormat + let result = case runPure $ reader (tabFilter 4 text) >>= writer of + Right s -> s Left err -> error (show err) let output = encode $ object [ T.pack "html" .= result , T.pack "name" .= @@ -50,52 +57,7 @@ writerOpts :: WriterOptions writerOpts = def { writerReferenceLinks = True, writerEmailObfuscation = NoObfuscation, writerHTMLMathMethod = MathJax "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML", - writerHighlight = True } + writerHighlightStyle = Just pygments } readerOpts :: ReaderOptions -readerOpts = def { readerParseRaw = True, - readerSmart = True } - -fromFormats :: [(Text, String -> Either PandocError Pandoc)] -fromFormats = [ - ("native" , readNative) - ,("json" , Text.Pandoc.readJSON readerOpts) - ,("markdown" , readMarkdown readerOpts) - ,("markdown_strict" , readMarkdown readerOpts{ - readerExtensions = strictExtensions, - readerSmart = False }) - ,("markdown_phpextra" , readMarkdown readerOpts{ - readerExtensions = phpMarkdownExtraExtensions }) - ,("markdown_github" , readMarkdown readerOpts{ - readerExtensions = githubMarkdownExtensions }) - ,("markdown_mmd", readMarkdown readerOpts{ - readerExtensions = multimarkdownExtensions }) - ,("rst" , readRST readerOpts) - ,("mediawiki" , readMediaWiki readerOpts) - ,("docbook" , readDocBook readerOpts) - ,("opml" , readOPML readerOpts) - ,("t2t" , readTxt2TagsNoMacros readerOpts) - ,("org" , readOrg readerOpts) - ,("textile" , readTextile readerOpts) -- TODO : textile+lhs - ,("html" , readHtml readerOpts) - ,("latex" , readLaTeX readerOpts) - ,("haddock" , readHaddock readerOpts) - ] - -toFormats :: [(Text, Pandoc -> String)] -toFormats = mapMaybe (\(x,y) -> - case y of - PureStringWriter w -> Just (T.pack x, w writerOpts{ - writerExtensions = - case x of - "markdown_strict" -> strictExtensions - "markdown_phpextra" -> phpMarkdownExtraExtensions - "markdown_mmd" -> multimarkdownExtensions - "markdown_github" -> githubMarkdownExtensions - _ -> pandocExtensions - }) - _ -> - case x of - "rtf" -> Just (T.pack x, writeRTF writerOpts) - _ -> Nothing) writers - +readerOpts = def |