1{-# LANGUAGE CPP, TupleSections #-}
3Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 Copyright : Copyright (C) 2006-2016 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
34import Text.Pandoc.Builder (setMeta)
35import Text.Pandoc.PDF (makePDF)
36import Text.Pandoc.Walk (walk)
37import Text.Pandoc.Readers.LaTeX (handleIncludes)
38import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
39 safeRead, headerShift, normalize, err, warn,
41import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag )
42import Text.Pandoc.XML ( toEntities )
43import Text.Pandoc.SelfContained ( makeSelfContained )
44import Text.Pandoc.Process (pipeProcess)
45import Text.Highlighting.Kate ( languages, Style, tango, pygments,
46 espresso, zenburn, kate, haddock, monochrome )
47import System.Environment ( getArgs, getProgName )
48import System.Exit ( ExitCode (..), exitSuccess )
50import System.Console.GetOpt
51import Data.Char ( toLower, toUpper )
52import Data.List ( delete, intercalate, isPrefixOf, isSuffixOf, sort )
53import System.Directory ( getAppUserDataDirectory, findExecutable,
54 doesFileExist, Permissions(..), getPermissions )
55import System.Process ( readProcessWithExitCode )
56import System.IO ( stdout, stderr )
57import System.IO.Error ( isDoesNotExistError )
58import qualified Control.Exception as E
59import Control.Exception.Extensible ( throwIO )
60import qualified Text.Pandoc.UTF8 as UTF8
61import Control.Monad (when, unless, (>=>))
62import Data.Maybe (fromMaybe, isNothing, isJust)
63import Data.Foldable (foldrM)
64import Network.URI (parseURI, isURI, URI(..))
65import qualified Data.ByteString.Lazy as B
66import qualified Data.ByteString as BS
67import Data.Aeson (eitherDecode', encode)
68import qualified Data.Map as M
69import Data.Yaml (decode)
70import qualified Data.Yaml as Yaml
71import qualified Data.Text as T
72import Control.Applicative ((<|>))
73import Text.Pandoc.Readers.Txt2Tags (getT2TMeta)
74import Paths_pandoc (getDataDir)
75import Text.Printf (printf)
76import Text.Pandoc.Error
78type Transform = Pandoc -> Pandoc
80copyrightMessage :: String
81copyrightMessage = intercalate "\n" [
83 "Copyright (C) 2006-2016 John MacFarlane",
84 "Web: http://pandoc.org",
85 "This is free software; see the source for copying conditions.",
86 "There is no warranty, not even for merchantability or fitness",
87 "for a particular purpose." ]
91 "\nCompiled with texmath " ++
92 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
93 ".\nSyntax highlighting is supported for the following languages:\n " ++
95 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
97-- | Converts a list of strings into a single string with the items printed as
98-- comma separated words in lines with a maximum line length.
99wrapWords :: Int -> Int -> [String] -> String
100wrapWords indent c = wrap' (c - indent) (c - indent)
103 wrap' cols remaining (x:xs)
104 | remaining == cols =
105 x ++ wrap' cols (remaining - length x) xs
106 | (length x + 1) > remaining =
107 ",\n" ++ replicate indent ' ' ++ x ++
108 wrap' cols (cols - length x) xs
111 wrap' cols (remaining - length x - 2) xs
113isTextFormat :: String -> Bool
114isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
116externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
117externalFilter f args' d = do
118 mbexe <- if '/' `elem` f
119 -- don't check PATH if filter name has a path
121 -- we catch isDoesNotExistError because this will
122 -- be triggered if PATH not set:
123 else E.catch (findExecutable f)
124 (\e -> if isDoesNotExistError e
127 (f', args'') <- case mbexe of
128 Just x -> return (x, args')
130 exists <- doesFileExist f
133 isExecutable <- executable `fmap`
136 case map toLower $ takeExtension f of
137 _ | isExecutable -> (f, args')
138 ".py" -> ("python", f:args')
139 ".hs" -> ("runhaskell", f:args')
140 ".pl" -> ("perl", f:args')
141 ".rb" -> ("ruby", f:args')
142 ".php" -> ("php", f:args')
144 else err 85 $ "Filter " ++ f ++ " not found"
146 mbExe <- findExecutable f'
147 when (isNothing mbExe) $
148 err 83 $ "Error running filter " ++ f ++ "\n" ++
149 show f' ++ " not found in path."
150 (exitcode, outbs, errbs) <- E.handle filterException $
151 pipeProcess Nothing f' args'' $ encode d
152 unless (B.null errbs) $ B.hPutStr stderr errbs
154 ExitSuccess -> return $ either error id $ eitherDecode' outbs
155 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
156 "Filter returned error status " ++ show ec
157 where filterException :: E.SomeException -> IO a
158 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
161-- | Data structure for command line options.
163 { optTabStop :: Int -- ^ Number of spaces per tab
164 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
165 , optStandalone :: Bool -- ^ Include header, footer
166 , optReader :: String -- ^ Reader format
167 , optWriter :: String -- ^ Writer format
168 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
169 , optTableOfContents :: Bool -- ^ Include table of contents
170 , optTransforms :: [Transform] -- ^ Doc transforms to apply
171 , optTemplate :: Maybe FilePath -- ^ Custom template
172 , optVariables :: [(String,String)] -- ^ Template variables to set
173 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
174 , optOutputFile :: String -- ^ Name of output file
175 , optNumberSections :: Bool -- ^ Number sections in LaTeX
176 , optNumberOffset :: [Int] -- ^ Starting number for sections
177 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
178 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
179 , optSelfContained :: Bool -- ^ Make HTML accessible offline
180 , optSmart :: Bool -- ^ Use smart typography
181 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
182 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
183 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
184 , optHighlight :: Bool -- ^ Highlight source code
185 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
186 , optChapters :: Bool -- ^ Use chapter for top-level sects
187 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
188 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
189 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
190 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
191 , optEpubMetadata :: String -- ^ EPUB metadata
192 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
193 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
194 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
195 , optDumpArgs :: Bool -- ^ Output command-line arguments
196 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
197 , optVerbose :: Bool -- ^ Verbose diagnostic output
198 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
199 , optDpi :: Int -- ^ Dpi
200 , optWrapText :: WrapOption -- ^ Options for wrapping text
201 , optColumns :: Int -- ^ Line length in characters
202 , optFilters :: [FilePath] -- ^ Filters to apply
203 , optEmailObfuscation :: ObfuscationMethod
204 , optIdentifierPrefix :: String
205 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
206 , optDataDir :: Maybe FilePath
207 , optCiteMethod :: CiteMethod -- ^ Method to output cites
208 , optListings :: Bool -- ^ Use listings package for code blocks
209 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
210 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
211 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
212 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
213 , optAscii :: Bool -- ^ Use ascii characters only in html
214 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
215 , optDefaultImageExtension :: String -- ^ Default image extension
216 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
217 , optTrace :: Bool -- ^ Print debug information
218 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
219 , optFileScope :: Bool -- ^ Parse input files before combining
220 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
221 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
224-- | Defaults for command-line options.
228 , optPreserveTabs = False
229 , optStandalone = False
230 , optReader = "" -- null for default reader
231 , optWriter = "" -- null for default writer
232 , optParseRaw = False
233 , optTableOfContents = False
235 , optTemplate = Nothing
237 , optMetadata = M.empty
238 , optOutputFile = "-" -- "-" means stdout
239 , optNumberSections = False
240 , optNumberOffset = [0,0,0,0,0,0]
241 , optSectionDivs = False
242 , optIncremental = False
243 , optSelfContained = False
245 , optOldDashes = False
247 , optHtmlQTags = False
248 , optHighlight = True
249 , optHighlightStyle = pygments
250 , optChapters = False
251 , optHTMLMathMethod = PlainMath
252 , optReferenceODT = Nothing
253 , optReferenceDocx = Nothing
254 , optEpubStylesheet = Nothing
255 , optEpubMetadata = ""
257 , optEpubChapterLevel = 1
259 , optDumpArgs = False
260 , optIgnoreArgs = False
262 , optReferenceLinks = False
264 , optWrapText = WrapAuto
267 , optEmailObfuscation = JavascriptObfuscation
268 , optIdentifierPrefix = ""
269 , optIndentedCodeClasses = []
270 , optDataDir = Nothing
271 , optCiteMethod = Citeproc
272 , optListings = False
273 , optLaTeXEngine = "pdflatex"
274 , optLaTeXEngineArgs = []
275 , optSlideLevel = Nothing
276 , optSetextHeaders = True
278 , optTeXLigatures = True
279 , optDefaultImageExtension = ""
280 , optExtractMedia = Nothing
282 , optTrackChanges = AcceptChanges
283 , optFileScope = False
284 , optKaTeXStylesheet = Nothing
285 , optKaTeXJS = Nothing
288-- | A list of functions, each transforming the options data structure
289-- in response to a command-line option.
290options :: [OptDescr (Opt -> IO Opt)]
292 [ Option "fr" ["from","read"]
294 (\arg opt -> return opt { optReader = arg })
298 , Option "tw" ["to","write"]
300 (\arg opt -> return opt { optWriter = arg })
304 , Option "o" ["output"]
306 (\arg opt -> return opt { optOutputFile = arg })
308 "" -- "Name of output file"
310 , Option "" ["data-dir"]
312 (\arg opt -> return opt { optDataDir = Just arg })
313 "DIRECTORY") -- "Directory containing pandoc data files."
316 , Option "R" ["parse-raw"]
318 (\opt -> return opt { optParseRaw = True }))
319 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
321 , Option "S" ["smart"]
323 (\opt -> return opt { optSmart = True }))
324 "" -- "Use smart quotes, dashes, and ellipses"
326 , Option "" ["old-dashes"]
328 (\opt -> return opt { optSmart = True
329 , optOldDashes = True }))
330 "" -- "Use smart quotes, dashes, and ellipses"
332 , Option "" ["base-header-level"]
337 let oldTransforms = optTransforms opt
339 return opt{ optTransforms =
340 headerShift shift : oldTransforms }
342 "base-header-level must be a number > 0")
344 "" -- "Headers base level"
346 , Option "" ["indented-code-classes"]
348 (\arg opt -> return opt { optIndentedCodeClasses = words $
349 map (\c -> if c == ',' then ' ' else c) arg })
351 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
353 , Option "F" ["filter"]
355 (\arg opt -> return opt { optFilters = arg : optFilters opt })
357 "" -- "External JSON filter"
359 , Option "" ["normalize"]
361 (\opt -> return opt { optTransforms =
362 normalize : optTransforms opt } ))
363 "" -- "Normalize the Pandoc AST"
365 , Option "p" ["preserve-tabs"]
367 (\opt -> return opt { optPreserveTabs = True }))
368 "" -- "Preserve tabs instead of converting to spaces"
370 , Option "" ["tab-stop"]
374 Just t | t > 0 -> return opt { optTabStop = t }
376 "tab-stop must be a number greater than 0")
378 "" -- "Tab stop (default 4)"
380 , Option "" ["track-changes"]
383 action <- case arg of
384 "accept" -> return AcceptChanges
385 "reject" -> return RejectChanges
386 "all" -> return AllChanges
388 ("Unknown option for track-changes: " ++ arg)
389 return opt { optTrackChanges = action })
391 "" -- "Accepting or reject MS Word track-changes.""
393 , Option "" ["file-scope"]
395 (\opt -> return opt { optFileScope = True }))
396 "" -- "Parse input files before combining"
398 , Option "" ["extract-media"]
401 return opt { optExtractMedia = Just arg })
403 "" -- "Directory to which to extract embedded media"
405 , Option "s" ["standalone"]
407 (\opt -> return opt { optStandalone = True }))
408 "" -- "Include needed header and footer on output"
410 , Option "" ["template"]
413 return opt{ optTemplate = Just arg,
414 optStandalone = True })
416 "" -- "Use custom template"
418 , Option "M" ["metadata"]
421 let (key,val) = case break (`elem` ":=") arg of
422 (k,_:v) -> (k, readMetaValue v)
423 (k,_) -> (k, MetaBool True)
424 return opt{ optMetadata = addMetadata key val
429 , Option "V" ["variable"]
432 let (key,val) = case break (`elem` ":=") arg of
435 return opt{ optVariables = (key,val) : optVariables opt })
439 , Option "D" ["print-default-template"]
442 templ <- getDefaultTemplate Nothing arg
444 Right t -> UTF8.hPutStr stdout t
445 Left e -> error $ show e
448 "" -- "Print default template for FORMAT"
450 , Option "" ["print-default-data-file"]
453 readDataFile Nothing arg >>= BS.hPutStr stdout
456 "" -- "Print default data file"
462 Just t | t > 0 -> return opt { optDpi = t }
464 "dpi must be a number greater than 0")
466 "" -- "Dpi (default 96)"
468 , Option "" ["no-wrap"]
470 (\opt -> do warn $ "--no-wrap is deprecated. " ++
471 "Use --wrap=none or --wrap=preserve instead."
472 return opt { optWrapText = WrapNone }))
478 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
479 Just o -> return opt { optWrapText = o }
480 Nothing -> err 77 "--wrap must be auto, none, or preserve")
481 "[auto|none|preserve]")
482 "" -- "Option for wrapping text in output"
484 , Option "" ["columns"]
488 Just t | t > 0 -> return opt { optColumns = t }
490 "columns must be a number greater than 0")
492 "" -- "Length of line in characters"
494 , Option "" ["toc", "table-of-contents"]
496 (\opt -> return opt { optTableOfContents = True }))
497 "" -- "Include table of contents"
499 , Option "" ["toc-depth"]
503 Just t | t >= 1 && t <= 6 ->
504 return opt { optTOCDepth = t }
506 "TOC level must be a number between 1 and 6")
508 "" -- "Number of levels to include in TOC"
510 , Option "" ["no-highlight"]
512 (\opt -> return opt { optHighlight = False }))
513 "" -- "Don't highlight source code"
515 , Option "" ["highlight-style"]
518 newStyle <- case map toLower arg of
519 "pygments" -> return pygments
520 "tango" -> return tango
521 "espresso" -> return espresso
522 "zenburn" -> return zenburn
523 "kate" -> return kate
524 "monochrome" -> return monochrome
525 "haddock" -> return haddock
527 "Unknown style :" ++ arg
528 return opt{ optHighlightStyle = newStyle })
530 "" -- "Style for highlighted code"
532 , Option "H" ["include-in-header"]
535 text <- UTF8.readFile arg
536 -- add new ones to end, so they're included in order specified
537 let newvars = optVariables opt ++ [("header-includes",text)]
538 return opt { optVariables = newvars,
539 optStandalone = True })
541 "" -- "File to include at end of header (implies -s)"
543 , Option "B" ["include-before-body"]
546 text <- UTF8.readFile arg
547 -- add new ones to end, so they're included in order specified
548 let newvars = optVariables opt ++ [("include-before",text)]
549 return opt { optVariables = newvars,
550 optStandalone = True })
552 "" -- "File to include before document body"
554 , Option "A" ["include-after-body"]
557 text <- UTF8.readFile arg
558 -- add new ones to end, so they're included in order specified
559 let newvars = optVariables opt ++ [("include-after",text)]
560 return opt { optVariables = newvars,
561 optStandalone = True })
563 "" -- "File to include after document body"
565 , Option "" ["self-contained"]
567 (\opt -> return opt { optSelfContained = True,
568 optStandalone = True }))
569 "" -- "Make slide shows include all the needed js and css"
571 , Option "" ["html-q-tags"]
574 return opt { optHtmlQTags = True }))
575 "" -- "Use <q> tags for quotes in HTML"
577 , Option "" ["ascii"]
579 (\opt -> return opt { optAscii = True }))
580 "" -- "Use ascii characters only in HTML output"
582 , Option "" ["reference-links"]
584 (\opt -> return opt { optReferenceLinks = True } ))
585 "" -- "Use reference links in parsing HTML"
587 , Option "" ["atx-headers"]
589 (\opt -> return opt { optSetextHeaders = False } ))
590 "" -- "Use atx-style headers for markdown"
592 , Option "" ["chapters"]
594 (\opt -> return opt { optChapters = True }))
595 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
597 , Option "N" ["number-sections"]
599 (\opt -> return opt { optNumberSections = True }))
600 "" -- "Number sections in LaTeX"
602 , Option "" ["number-offset"]
605 case safeRead ('[':arg ++ "]") of
606 Just ns -> return opt { optNumberOffset = ns,
607 optNumberSections = True }
608 _ -> err 57 "could not parse number-offset")
610 "" -- "Starting number for sections, subsections, etc."
612 , Option "" ["no-tex-ligatures"]
614 (\opt -> return opt { optTeXLigatures = False }))
615 "" -- "Don't use tex ligatures for quotes, dashes"
617 , Option "" ["listings"]
619 (\opt -> return opt { optListings = True }))
620 "" -- "Use listings package for LaTeX code blocks"
622 , Option "i" ["incremental"]
624 (\opt -> return opt { optIncremental = True }))
625 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
627 , Option "" ["slide-level"]
631 Just t | t >= 1 && t <= 6 ->
632 return opt { optSlideLevel = Just t }
634 "slide level must be a number between 1 and 6")
636 "" -- "Force header level for slides"
638 , Option "" ["section-divs"]
640 (\opt -> return opt { optSectionDivs = True }))
641 "" -- "Put sections in div tags in HTML"
643 , Option "" ["default-image-extension"]
645 (\arg opt -> return opt { optDefaultImageExtension = arg })
647 "" -- "Default extension for extensionless images"
649 , Option "" ["email-obfuscation"]
652 method <- case arg of
653 "references" -> return ReferenceObfuscation
654 "javascript" -> return JavascriptObfuscation
655 "none" -> return NoObfuscation
657 ("Unknown obfuscation method: " ++ arg)
658 return opt { optEmailObfuscation = method })
659 "none|javascript|references")
660 "" -- "Method for obfuscating email in HTML"
662 , Option "" ["id-prefix"]
664 (\arg opt -> return opt { optIdentifierPrefix = arg })
666 "" -- "Prefix to add to automatically generated HTML identifiers"
668 , Option "T" ["title-prefix"]
671 let newvars = ("title-prefix", arg) : optVariables opt
672 return opt { optVariables = newvars,
673 optStandalone = True })
675 "" -- "String to prefix to HTML window title"
680 -- add new link to end, so it is included in proper order
681 let newvars = optVariables opt ++ [("css",arg)]
682 return opt { optVariables = newvars,
683 optStandalone = True })
685 "" -- "Link to CSS style sheet"
687 , Option "" ["reference-odt"]
690 return opt { optReferenceODT = Just arg })
692 "" -- "Path of custom reference.odt"
694 , Option "" ["reference-docx"]
697 return opt { optReferenceDocx = Just arg })
699 "" -- "Path of custom reference.docx"
701 , Option "" ["epub-stylesheet"]
704 text <- UTF8.readFile arg
705 return opt { optEpubStylesheet = Just text })
707 "" -- "Path of epub.css"
709 , Option "" ["epub-cover-image"]
712 return opt { optVariables =
713 ("epub-cover-image", arg) : optVariables opt })
715 "" -- "Path of epub cover image"
717 , Option "" ["epub-metadata"]
720 text <- UTF8.readFile arg
721 return opt { optEpubMetadata = text })
723 "" -- "Path of epub metadata file"
725 , Option "" ["epub-embed-font"]
728 return opt{ optEpubFonts = arg : optEpubFonts opt })
730 "" -- "Directory of fonts to embed"
732 , Option "" ["epub-chapter-level"]
736 Just t | t >= 1 && t <= 6 ->
737 return opt { optEpubChapterLevel = t }
739 "chapter level must be a number between 1 and 6")
741 "" -- "Header level at which to split chapters in EPUB"
743 , Option "" ["latex-engine"]
746 let b = takeBaseName arg
747 if b `elem` ["pdflatex", "lualatex", "xelatex"]
748 then return opt { optLaTeXEngine = arg }
749 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
751 "" -- "Name of latex program to use in generating PDF"
753 , Option "" ["latex-engine-opt"]
756 let oldArgs = optLaTeXEngineArgs opt
757 return opt { optLaTeXEngineArgs = arg : oldArgs })
759 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
761 , Option "" ["bibliography"]
763 (\arg opt -> return opt{ optMetadata = addMetadata
764 "bibliography" (readMetaValue arg)
773 return opt{ optMetadata = addMetadata "csl"
779 , Option "" ["citation-abbreviations"]
782 return opt{ optMetadata = addMetadata
783 "citation-abbreviations"
789 , Option "" ["natbib"]
791 (\opt -> return opt { optCiteMethod = Natbib }))
792 "" -- "Use natbib cite commands in LaTeX output"
794 , Option "" ["biblatex"]
796 (\opt -> return opt { optCiteMethod = Biblatex }))
797 "" -- "Use biblatex cite commands in LaTeX output"
799 , Option "m" ["latexmathml", "asciimathml"]
802 return opt { optHTMLMathMethod = LaTeXMathML arg })
804 "" -- "Use LaTeXMathML script in html output"
806 , Option "" ["mathml"]
809 return opt { optHTMLMathMethod = MathML arg })
811 "" -- "Use mathml for HTML math"
813 , Option "" ["mimetex"]
816 let url' = case arg of
818 Nothing -> "/cgi-bin/mimetex.cgi?"
819 return opt { optHTMLMathMethod = WebTeX url' })
821 "" -- "Use mimetex for HTML math"
823 , Option "" ["webtex"]
826 let url' = fromMaybe "http://chart.apis.google.com/chart?cht=tx&chl=" arg
827 return opt { optHTMLMathMethod = WebTeX url' })
829 "" -- "Use web service for HTML math"
831 , Option "" ["jsmath"]
833 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
835 "" -- "Use jsMath for HTML math"
837 , Option "" ["mathjax"]
840 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
841 return opt { optHTMLMathMethod = MathJax url'})
843 "" -- "Use MathJax for HTML math"
844 , Option "" ["katex"]
849 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.js"})
851 "" -- Use KaTeX for HTML Math
853 , Option "" ["katex-stylesheet"]
856 return opt { optKaTeXStylesheet = Just arg })
858 "" -- Set the KaTeX Stylesheet location
860 , Option "" ["gladtex"]
862 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
863 "" -- "Use gladtex for HTML math"
865 , Option "" ["trace"]
867 (\opt -> return opt { optTrace = True }))
868 "" -- "Turn on diagnostic tracing in readers."
870 , Option "" ["dump-args"]
872 (\opt -> return opt { optDumpArgs = True }))
873 "" -- "Print output filename and arguments to stdout."
875 , Option "" ["ignore-args"]
877 (\opt -> return opt { optIgnoreArgs = True }))
878 "" -- "Ignore command-line arguments."
880 , Option "" ["verbose"]
882 (\opt -> return opt { optVerbose = True }))
883 "" -- "Verbose diagnostic output."
885 , Option "" ["bash-completion"]
889 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
890 let optnames (Option shorts longs _ _) =
891 map (\c -> ['-',c]) shorts ++
893 let allopts = unwords (concatMap optnames options)
894 UTF8.hPutStrLn stdout $ printf tpl allopts
895 (unwords (map fst readers))
896 (unwords ("pdf": map fst writers))
899 "" -- "Print bash completion script"
901 , Option "v" ["version"]
905 defaultDatadir <- getAppUserDataDirectory "pandoc"
906 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
907 compileInfo ++ "\nDefault user data directory: " ++
908 defaultDatadir ++ copyrightMessage)
910 "" -- "Print version"
912 , Option "h" ["help"]
916 UTF8.hPutStr stdout (usageMessage prg options)
923addMetadata :: String -> MetaValue -> M.Map String MetaValue
924 -> M.Map String MetaValue
925addMetadata k v m = case M.lookup k m of
926 Nothing -> M.insert k v m
927 Just (MetaList xs) -> M.insert k
928 (MetaList (xs ++ [v])) m
929 Just x -> M.insert k (MetaList [v, x]) m
931readMetaValue :: String -> MetaValue
932readMetaValue s = case decode (UTF8.fromString s) of
933 Just (Yaml.String t) -> MetaString $ T.unpack t
934 Just (Yaml.Bool b) -> MetaBool b
937-- Returns usage message
938usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
939usageMessage programName = usageInfo
940 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
941 wrapWords 16 78 readers'names ++
942 '\n' : replicate 16 ' ' ++
943 "[ *only Pandoc's JSON version of native AST]" ++ "\nOutput formats: " ++
944 wrapWords 16 78 writers'names ++
945 '\n' : replicate 16 ' ' ++
946 "[**for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
948 writers'names = sort $ "json*" : "pdf**" : delete "json" (map fst writers)
949 readers'names = sort $ "json*" : delete "json" (map fst readers)
951-- Determine default reader based on source file extensions
952defaultReaderName :: String -> [FilePath] -> String
953defaultReaderName fallback [] = fallback
954defaultReaderName fallback (x:xs) =
955 case takeExtension (map toLower x) of
964 ".lhs" -> "markdown+lhs"
967 ".wiki" -> "mediawiki"
968 ".dokuwiki" -> "dokuwiki"
969 ".textile" -> "textile"
970 ".native" -> "native"
976 ".pdf" -> "pdf" -- so we get an "unknown reader" error
977 ".doc" -> "doc" -- so we get an "unknown reader" error
978 _ -> defaultReaderName fallback xs
980-- Returns True if extension of first source is .lhs
981lhsExtension :: [FilePath] -> Bool
982lhsExtension (x:_) = takeExtension x == ".lhs"
983lhsExtension _ = False
985-- Determine default writer based on output file extension
986defaultWriterName :: FilePath -> String
987defaultWriterName "-" = "html" -- no output file
989 case takeExtension (map toLower x) of
990 "" -> "markdown" -- empty extension
994 ".context" -> "context"
999 ".native" -> "native"
1001 ".txt" -> "markdown"
1002 ".text" -> "markdown"
1004 ".markdown" -> "markdown"
1005 ".textile" -> "textile"
1006 ".lhs" -> "markdown+lhs"
1007 ".texi" -> "texinfo"
1008 ".texinfo" -> "texinfo"
1014 ".asciidoc" -> "asciidoc"
1015 ".adoc" -> "asciidoc"
1022 ['.',y] | y `elem` ['1'..'9'] -> "man"
1025-- Transformations of a Pandoc document post-parsing:
1027extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1028extractMedia media dir d =
1029 case [fp | (fp, _, _) <- mediaDirectory media] of
1032 extractMediaBag True dir media
1033 return $ walk (adjustImagePath dir fps) d
1035adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1036adjustImagePath dir paths (Image attr lab (src, tit))
1037 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1038adjustImagePath _ _ x = x
1040adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1041adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1043applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1044applyTransforms transforms d = return $ foldr ($) d transforms
1046applyFilters :: [FilePath] -> [String] -> Pandoc -> IO Pandoc
1047applyFilters filters args d =
1048 foldrM ($) d $ map (flip externalFilter args) filters
1050uppercaseFirstLetter :: String -> String
1051uppercaseFirstLetter (c:cs) = toUpper c : cs
1052uppercaseFirstLetter [] = []
1057 rawArgs <- map UTF8.decodeArg <$> getArgs
1060 let (actions, args, errors) = getOpt Permute options rawArgs
1062 unless (null errors) $
1063 err 2 $ concat $ errors ++
1064 ["Try " ++ prg ++ " --help for more information."]
1066 -- thread option data structure through all supplied option actions
1067 opts <- foldl (>>=) (return defaultOpts) actions
1068 convertWithOpts opts args
1070convertWithOpts :: Opt -> [FilePath] -> IO ()
1071convertWithOpts opts args = do
1072 let Opt { optTabStop = tabStop
1073 , optPreserveTabs = preserveTabs
1074 , optStandalone = standalone
1075 , optReader = readerName
1076 , optWriter = writerName
1077 , optParseRaw = parseRaw
1078 , optVariables = variables
1079 , optMetadata = metadata
1080 , optTableOfContents = toc
1081 , optTransforms = transforms
1082 , optTemplate = templatePath
1083 , optOutputFile = outputFile
1084 , optNumberSections = numberSections
1085 , optNumberOffset = numberFrom
1086 , optSectionDivs = sectionDivs
1087 , optIncremental = incremental
1088 , optSelfContained = selfContained
1090 , optOldDashes = oldDashes
1092 , optHtmlQTags = htmlQTags
1093 , optHighlight = highlight
1094 , optHighlightStyle = highlightStyle
1095 , optChapters = chapters
1096 , optHTMLMathMethod = mathMethod'
1097 , optReferenceODT = referenceODT
1098 , optReferenceDocx = referenceDocx
1099 , optEpubStylesheet = epubStylesheet
1100 , optEpubMetadata = epubMetadata
1101 , optEpubFonts = epubFonts
1102 , optEpubChapterLevel = epubChapterLevel
1103 , optTOCDepth = epubTOCDepth
1104 , optDumpArgs = dumpArgs
1105 , optIgnoreArgs = ignoreArgs
1106 , optVerbose = verbose
1107 , optReferenceLinks = referenceLinks
1109 , optWrapText = wrap
1110 , optColumns = columns
1111 , optFilters = filters
1112 , optEmailObfuscation = obfuscationMethod
1113 , optIdentifierPrefix = idPrefix
1114 , optIndentedCodeClasses = codeBlockClasses
1115 , optDataDir = mbDataDir
1116 , optCiteMethod = citeMethod
1117 , optListings = listings
1118 , optLaTeXEngine = latexEngine
1119 , optLaTeXEngineArgs = latexEngineArgs
1120 , optSlideLevel = slideLevel
1121 , optSetextHeaders = setextHeaders
1123 , optTeXLigatures = texLigatures
1124 , optDefaultImageExtension = defaultImageExtension
1125 , optExtractMedia = mbExtractMedia
1127 , optTrackChanges = trackChanges
1128 , optFileScope = fileScope
1129 , optKaTeXStylesheet = katexStylesheet
1130 , optKaTeXJS = katexJS
1134 do UTF8.hPutStrLn stdout outputFile
1135 mapM_ (UTF8.hPutStrLn stdout) args
1138 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css"
1140 case (katexJS, katexStylesheet) of
1141 (Nothing, _) -> mathMethod'
1142 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1145 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1146 let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1147 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1148 "pandoc-citeproc" `notElem` map takeBaseName filters
1149 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1152 let sources = if ignoreArgs then [] else args
1154 datadir <- case mbDataDir of
1156 (Just <$> getAppUserDataDirectory "pandoc")
1157 (\e -> let _ = (e :: E.SomeException)
1159 Just _ -> return mbDataDir
1161 -- assign reader and writer based on options and filenames
1162 let readerName' = case map toLower readerName of
1163 [] -> defaultReaderName
1164 (if any isURI sources
1166 else "markdown") sources
1170 let writerName' = case map toLower writerName of
1171 [] -> defaultWriterName outputFile
1175 let format = takeWhile (`notElem` ['+','-'])
1176 $ takeFileName writerName' -- in case path to lua script
1178 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1180 let laTeXOutput = format `elem` ["latex", "beamer"]
1181 let conTeXtOutput = format == "context"
1182 let html5Output = format == "html5"
1184 let laTeXInput = "latex" `isPrefixOf` readerName' ||
1185 "beamer" `isPrefixOf` readerName'
1187 writer <- if ".lua" `isSuffixOf` format
1188 -- note: use non-lowercased version writerName
1189 then return $ IOStringWriter $ writeCustom writerName
1190 else case getWriter writerName' of
1194 "\nTo create a pdf with pandoc, use " ++
1195 "the latex or beamer writer and specify\n" ++
1196 "an output file with .pdf extension " ++
1197 "(pandoc -t latex -o filename.pdf)."
1201 reader <- if "t2t" == readerName'
1202 then (mkStringReader .
1204 getT2TMeta sources outputFile
1205 else case getReader readerName' of
1208 where e' = case readerName' of
1210 "\nPandoc can convert to PDF, but not from PDF."
1212 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1215 let standalone' = standalone || not (isTextFormat format) || pdfOutput
1217 templ <- case templatePath of
1218 _ | not standalone' -> return ""
1220 deftemp <- getDefaultTemplate datadir format
1225 -- strip off extensions
1226 let tp' = case takeExtension tp of
1229 E.catch (UTF8.readFile tp')
1230 (\e -> if isDoesNotExistError e
1232 (readDataFileUTF8 datadir
1233 ("templates" </> tp'))
1234 (\e' -> let _ = (e' :: E.SomeException)
1238 variables' <- case mathMethod of
1239 LaTeXMathML Nothing -> do
1240 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1241 return $ ("mathml-script", s) : variables
1242 MathML Nothing -> do
1243 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1244 return $ ("mathml-script", s) : variables
1245 _ -> return variables
1247 variables'' <- if format == "dzslides"
1249 dztempl <- readDataFileUTF8 datadir
1250 ("dzslides" </> "template.html")
1251 let dzline = "<!-- {{{{ dzslides core"
1252 let dzcore = unlines
1253 $ dropWhile (not . (dzline `isPrefixOf`))
1255 return $ ("dzslides-core", dzcore) : variables'
1256 else return variables'
1258 let sourceURL = case sources of
1260 (x:_) -> case parseURI x of
1262 | uriScheme u `elem` ["http:","https:"] ->
1263 Just $ show u{ uriQuery = "",
1267 let readerOpts = def{ readerSmart = if laTeXInput
1269 else smart || (texLigatures &&
1270 (laTeXOutput || conTeXtOutput))
1271 , readerStandalone = standalone'
1272 , readerParseRaw = parseRaw
1273 , readerColumns = columns
1274 , readerTabStop = tabStop
1275 , readerOldDashes = oldDashes
1276 , readerIndentedCodeClasses = codeBlockClasses
1277 , readerApplyMacros = not laTeXOutput
1278 , readerDefaultImageExtension = defaultImageExtension
1279 , readerTrace = trace
1280 , readerTrackChanges = trackChanges
1281 , readerFileScope = fileScope
1284 when (not (isTextFormat format) && outputFile == "-") $
1285 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1286 "Specify an output file using the -o option."
1288 let readSources [] = mapM readSource ["-"]
1289 readSources srcs = mapM readSource srcs
1290 readSource "-" = UTF8.getContents
1291 readSource src = case parseURI src of
1292 Just u | uriScheme u `elem` ["http:","https:"] ->
1294 _ -> UTF8.readFile src
1299 Right (bs,_) -> return $ UTF8.toString bs
1301 let readFiles [] = error "Cannot read archive from stdin"
1302 readFiles [x] = B.readFile x
1303 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1305 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1309 let handleIncludes' :: String -> IO (Either PandocError String)
1310 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1314 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1315 sourceToDoc sources' = fmap handleError $
1318 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1319 doc <- handleIncludes' srcs
1320 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1321 ByteStringReader r -> readFiles sources' >>= r readerOpts
1323 -- We parse first if (1) fileScope is set, (2), it's a binary
1324 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1325 -- of negatives as opposed to an OR of positives, so we do default
1326 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1328 (doc, media) <- case reader of
1329 (StringReader _) | not fileScope && readerName' /= "json" ->
1331 _ | null sources -> sourceToDoc sources
1332 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1333 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1335 let writerOptions = def { writerStandalone = standalone',
1336 writerTemplate = templ,
1337 writerVariables = variables'',
1338 writerTabStop = tabStop,
1339 writerTableOfContents = toc,
1340 writerHTMLMathMethod = mathMethod,
1341 writerIncremental = incremental,
1342 writerCiteMethod = citeMethod,
1343 writerIgnoreNotes = False,
1344 writerNumberSections = numberSections,
1345 writerNumberOffset = numberFrom,
1346 writerSectionDivs = sectionDivs,
1347 writerReferenceLinks = referenceLinks,
1349 writerWrapText = wrap,
1350 writerColumns = columns,
1351 writerEmailObfuscation = obfuscationMethod,
1352 writerIdentifierPrefix = idPrefix,
1353 writerSourceURL = sourceURL,
1354 writerUserDataDir = datadir,
1355 writerHtml5 = html5,
1356 writerHtmlQTags = htmlQTags,
1357 writerChapters = chapters,
1358 writerListings = listings,
1359 writerBeamer = False,
1360 writerSlideLevel = slideLevel,
1361 writerHighlight = highlight,
1362 writerHighlightStyle = highlightStyle,
1363 writerSetextHeaders = setextHeaders,
1364 writerTeXLigatures = texLigatures,
1365 writerEpubMetadata = epubMetadata,
1366 writerEpubStylesheet = epubStylesheet,
1367 writerEpubFonts = epubFonts,
1368 writerEpubChapterLevel = epubChapterLevel,
1369 writerTOCDepth = epubTOCDepth,
1370 writerReferenceODT = referenceODT,
1371 writerReferenceDocx = referenceDocx,
1372 writerMediaBag = media,
1373 writerVerbose = verbose,
1374 writerLaTeXArgs = latexEngineArgs
1378 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1379 adjustMetadata metadata >=>
1380 applyTransforms transforms >=>
1381 applyFilters filters' [format]) doc
1383 let writeBinary :: B.ByteString -> IO ()
1384 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1386 let writerFn :: FilePath -> String -> IO ()
1387 writerFn "-" = UTF8.putStr
1388 writerFn f = UTF8.writeFile f
1391 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1392 IOByteStringWriter f -> f writerOptions doc' >>= writeBinary
1395 -- make sure writer is latex or beamer or context or html5
1396 unless (laTeXOutput || conTeXtOutput || html5Output) $
1397 err 47 $ "cannot produce pdf output with " ++ format ++
1400 let pdfprog = case () of
1401 _ | conTeXtOutput -> "context"
1402 _ | html5Output -> "wkhtmltopdf"
1404 -- check for pdf creating program
1405 (ec,_,_) <- readProcessWithExitCode pdfprog ["--version"] ""
1406 when (ec /= ExitSuccess) $
1407 err 41 $ pdfprog ++ " not found. " ++
1408 pdfprog ++ " is needed for pdf output."
1410 res <- makePDF pdfprog f writerOptions doc'
1412 Right pdf -> writeBinary pdf
1414 B.hPutStr stderr err'
1415 B.hPut stderr $ B.pack [10]
1416 err 43 "Error producing PDF"
1417 | otherwise -> selfcontain (f writerOptions doc' ++
1418 ['\n' | not standalone'])
1419 >>= writerFn outputFile . handleEntities
1420 where htmlFormat = format `elem`
1421 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1422 selfcontain = if selfContained && htmlFormat
1423 then makeSelfContained writerOptions
1425 handleEntities = if htmlFormat && ascii
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list