2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE TupleSections #-}
5Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 Module : Text.Pandoc.App
24 Copyright : Copyright (C) 2006-2016 John MacFarlane
25 License : GNU GPL, version 2 or above
27 Maintainer : John MacFarlane <jgm@berkeley@edu>
29 Portability : portable
31Does a pandoc conversion based on command-line options.
33module Text.Pandoc.App (
40import Control.Applicative ((<|>))
41import qualified Control.Exception as E
42import Control.Exception.Extensible (throwIO)
44import Control.Monad.Trans
45import Data.Aeson (eitherDecode', encode)
46import qualified Data.ByteString as BS
47import qualified Data.ByteString.Lazy as B
48import Data.Char (toLower, toUpper)
49import qualified Data.Set as Set
50import Data.Foldable (foldrM)
51import Data.List (intercalate, isPrefixOf, isSuffixOf, sort)
52import qualified Data.Map as M
53import Data.Maybe (fromMaybe, isJust, isNothing)
54import qualified Data.Text as T
55import Data.Yaml (decode)
56import qualified Data.Yaml as Yaml
57import Network.URI (URI (..), isURI, parseURI)
58import Paths_pandoc (getDataDir)
59import Skylighting (Style, Syntax (..), defaultSyntaxMap)
60import System.Console.GetOpt
61import System.Directory (Permissions (..), doesFileExist, findExecutable,
62 getAppUserDataDirectory, getPermissions)
63import System.Environment (getArgs, getEnvironment, getProgName)
64import System.Exit (ExitCode (..), exitSuccess)
66import System.IO (stderr, stdout)
67import System.IO.Error (isDoesNotExistError)
69import Text.Pandoc.Builder (setMeta)
70import Text.Pandoc.Class (PandocIO, getLog, withMediaBag)
71import Text.Pandoc.Highlighting (highlightingStyles)
72import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory)
73import Text.Pandoc.PDF (makePDF)
74import Text.Pandoc.Process (pipeProcess)
75import Text.Pandoc.SelfContained (makeSelfContained)
76import Text.Pandoc.Shared (err, headerShift, openURL, readDataFile,
77 readDataFileUTF8, safeRead, tabFilter)
78import qualified Text.Pandoc.UTF8 as UTF8
79import Text.Pandoc.Walk (walk)
80import Text.Pandoc.XML (toEntities)
83import System.Posix.IO (stdOutput)
84import System.Posix.Terminal (queryTerminal)
87parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt
88parseOptions options' defaults = do
89 rawArgs <- map UTF8.decodeArg <$> getArgs
92 let (actions, args, unrecognizedOpts, errors) =
93 getOpt' Permute options' rawArgs
95 let unknownOptionErrors = foldr handleUnrecognizedOption [] unrecognizedOpts
97 unless (null errors && null unknownOptionErrors) $
98 err 2 $ concat errors ++ unlines unknownOptionErrors ++
99 ("Try " ++ prg ++ " --help for more information.")
101 -- thread option data structure through all supplied option actions
102 opts <- foldl (>>=) (return defaults) actions
103 return (opts{ optInputFiles = args })
105convertWithOpts :: Opt -> IO ()
106convertWithOpts opts = do
107 let args = optInputFiles opts
108 let outputFile = optOutputFile opts
109 let filters = optFilters opts
110 let verbosity = optVerbosity opts
112 when (optDumpArgs opts) $
113 do UTF8.hPutStrLn stdout outputFile
114 mapM_ (UTF8.hPutStrLn stdout) args
117 epubMetadata <- case optEpubMetadata opts of
118 Nothing -> return Nothing
119 Just fp -> Just <$> UTF8.readFile fp
121 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
123 case (optKaTeXJS opts, optKaTeXStylesheet opts) of
124 (Nothing, _) -> optHTMLMathMethod opts
125 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
128 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
129 let needsCiteproc = isJust (lookup "bibliography" (optMetadata opts)) &&
130 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
131 "pandoc-citeproc" `notElem` map takeBaseName filters
132 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
135 let sources = case args of
137 xs | optIgnoreArgs opts -> ["-"]
140 datadir <- case optDataDir opts of
142 (Just <$> getAppUserDataDirectory "pandoc")
143 (\e -> let _ = (e :: E.SomeException)
145 Just _ -> return $ optDataDir opts
147 -- assign reader and writer based on options and filenames
148 let readerName = case optReader opts of
149 Nothing -> defaultReaderName
150 (if any isURI sources
152 else "markdown") sources
153 Just x -> map toLower x
155 let writerName = case optWriter opts of
156 Nothing -> defaultWriterName outputFile
157 Just x -> map toLower x
158 let format = takeWhile (`notElem` ['+','-'])
159 $ takeFileName writerName -- in case path to lua script
161 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
163 let laTeXOutput = format `elem` ["latex", "beamer"]
164 let conTeXtOutput = format == "context"
165 let html5Output = format == "html5" || format == "html"
167 -- disabling the custom writer for now
168 writer <- if ".lua" `isSuffixOf` format
169 -- note: use non-lowercased version writerName
170 then return (StringWriter
171 (\o d -> liftIO $ writeCustom writerName o d)
173 else case getWriter writerName of
177 "\nTo create a pdf with pandoc, use " ++
178 "the latex or beamer writer and specify\n" ++
179 "an output file with .pdf extension " ++
180 "(pandoc -t latex -o filename.pdf)."
182 Right w -> return (w :: Writer PandocIO)
184 -- TODO: we have to get the input and the output into the state for
185 -- the sake of the text2tags reader.
186 reader <- case getReader readerName of
187 Right r -> return (r :: Reader PandocIO)
189 where e' = case readerName of
191 "\nPandoc can convert to PDF, but not from PDF."
193 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
196 let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
198 templ <- case optTemplate opts of
199 _ | not standalone -> return Nothing
201 deftemp <- getDefaultTemplate datadir format
204 Right t -> return (Just t)
206 -- strip off extensions
207 let tp' = case takeExtension tp of
210 Just <$> E.catch (UTF8.readFile tp')
211 (\e -> if isDoesNotExistError e
213 (readDataFileUTF8 datadir
214 ("templates" </> tp'))
215 (\e' -> let _ = (e' :: E.SomeException)
219 let addStringAsVariable varname s vars = return $ (varname, s) : vars
221 let addContentsAsVariable varname fp vars = do
222 s <- UTF8.readFile fp
223 return $ (varname, s) : vars
225 -- note: this reverses the list constructed in option parsing,
226 -- which in turn was reversed from the command-line order,
227 -- so we end up with the correct order in the variable list:
228 let withList _ [] vars = return vars
229 withList f (x:xs) vars = f x vars >>= withList f xs
232 return (("outputfile", optOutputFile opts) : optVariables opts)
234 withList (addStringAsVariable "sourcefile")
235 (reverse $ optInputFiles opts)
236 -- we reverse this list because, unlike
237 -- the other option lists here, it is
238 -- not reversed when parsed from CLI arguments.
239 -- See withList, above.
241 withList (addContentsAsVariable "include-before")
242 (optIncludeBeforeBody opts)
244 withList (addContentsAsVariable "include-after")
245 (optIncludeAfterBody opts)
247 withList (addContentsAsVariable "header-includes")
248 (optIncludeInHeader opts)
250 withList (addStringAsVariable "css") (optCss opts)
252 maybe return (addStringAsVariable "title-prefix") (optTitlePrefix opts)
254 maybe return (addStringAsVariable "epub-cover-image")
255 (optEpubCoverImage opts)
257 (\vars -> case mathMethod of
258 LaTeXMathML Nothing -> do
259 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
260 return $ ("mathml-script", s) : vars
263 (\vars -> if format == "dzslides"
265 dztempl <- readDataFileUTF8 datadir
266 ("dzslides" </> "template.html")
267 let dzline = "<!-- {{{{ dzslides core"
269 $ dropWhile (not . (dzline `isPrefixOf`))
271 return $ ("dzslides-core", dzcore) : vars
274 let sourceURL = case sources of
276 (x:_) -> case parseURI x of
278 | uriScheme u `elem` ["http:","https:"] ->
279 Just $ show u{ uriQuery = "",
283 abbrevs <- (Set.fromList . filter (not . null) . lines) <$>
284 case optAbbreviations opts of
285 Nothing -> readDataFileUTF8 datadir "abbreviations"
286 Just f -> UTF8.readFile f
288 let readerOpts = def{ readerStandalone = standalone
289 , readerColumns = optColumns opts
290 , readerTabStop = optTabStop opts
291 , readerIndentedCodeClasses = optIndentedCodeClasses opts
292 , readerApplyMacros = not laTeXOutput
293 , readerDefaultImageExtension =
294 optDefaultImageExtension opts
295 , readerTrackChanges = optTrackChanges opts
296 , readerAbbreviations = abbrevs
299 highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts
301 let writerOptions = def { writerTemplate = templ,
302 writerVariables = variables,
303 writerTabStop = optTabStop opts,
304 writerTableOfContents = optTableOfContents opts,
305 writerHTMLMathMethod = mathMethod,
306 writerIncremental = optIncremental opts,
307 writerCiteMethod = optCiteMethod opts,
308 writerNumberSections = optNumberSections opts,
309 writerNumberOffset = optNumberOffset opts,
310 writerSectionDivs = optSectionDivs opts,
311 writerReferenceLinks = optReferenceLinks opts,
312 writerReferenceLocation = optReferenceLocation opts,
313 writerDpi = optDpi opts,
314 writerWrapText = optWrapText opts,
315 writerColumns = optColumns opts,
316 writerEmailObfuscation = optEmailObfuscation opts,
317 writerIdentifierPrefix = optIdentifierPrefix opts,
318 writerSourceURL = sourceURL,
319 writerUserDataDir = datadir,
320 writerHtmlQTags = optHtmlQTags opts,
321 writerTopLevelDivision = optTopLevelDivision opts,
322 writerListings = optListings opts,
323 writerSlideLevel = optSlideLevel opts,
324 writerHighlightStyle = highlightStyle,
325 writerSetextHeaders = optSetextHeaders opts,
326 writerEpubMetadata = epubMetadata,
327 writerEpubFonts = optEpubFonts opts,
328 writerEpubChapterLevel = optEpubChapterLevel opts,
329 writerTOCDepth = optTOCDepth opts,
330 writerReferenceDoc = optReferenceDoc opts,
331 writerLaTeXArgs = optLaTeXEngineArgs opts
338 istty <- queryTerminal stdOutput
340 when (istty && not (isTextFormat format) && outputFile == "-") $
341 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
342 "Specify an output file using the -o option."
345 let transforms = case optBaseHeaderLevel opts of
346 x | x > 1 -> [headerShift (x - 1)]
349 let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t"
351 else optTabStop opts)
353 readSources :: (Functor m, MonadIO m) => [FilePath] -> m String
354 readSources srcs = convertTabs . intercalate "\n" <$>
357 let runIO' :: PandocIO a -> IO a
359 (res, reports) <- runIOorExplode $ do
360 setVerbosity verbosity
364 case optLogFile opts of
366 Just logfile -> B.writeFile logfile (encodeLogMessages reports)
367 let isWarning msg = messageVerbosity msg == WARNING
368 when (optFailIfWarnings opts && any isWarning reports) $
369 err 3 "Failing because there were warnings."
372 let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag)
373 sourceToDoc sources' =
376 | optFileScope opts || readerName == "json" -> do
378 (readSource >=> withMediaBag . r readerOpts) sources
379 return (mconcat (map fst pairs), mconcat (map snd pairs))
381 readSources sources' >>= withMediaBag . r readerOpts
382 ByteStringReader r -> do
383 pairs <- mapM (readFile' >=>
384 withMediaBag . r readerOpts) sources
385 return (mconcat (map fst pairs), mconcat (map snd pairs))
388 (doc, media) <- sourceToDoc sources
389 doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=>
390 return . flip (foldr addMetadata) (optMetadata opts) >=>
391 applyTransforms transforms >=>
392 applyFilters datadir filters' [format]) doc
395 -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile
396 ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
399 -- make sure writer is latex or beamer or context or html5
400 unless (laTeXOutput || conTeXtOutput || html5Output) $
401 err 47 $ "cannot produce pdf output with " ++ format ++
404 let pdfprog = case () of
405 _ | conTeXtOutput -> "context"
406 _ | html5Output -> "wkhtmltopdf"
407 _ -> optLaTeXEngine opts
408 -- check for pdf creating program
409 mbPdfProg <- liftIO $ findExecutable pdfprog
410 when (isNothing mbPdfProg) $
411 err 41 $ pdfprog ++ " not found. " ++
412 pdfprog ++ " is needed for pdf output."
414 res <- makePDF pdfprog f writerOptions verbosity media doc'
416 Right pdf -> writeFnBinary outputFile pdf
417 Left err' -> liftIO $ do
418 B.hPutStr stderr err'
419 B.hPut stderr $ B.pack [10]
420 err 43 "Error producing PDF"
422 let htmlFormat = format `elem`
423 ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"]
424 selfcontain = if optSelfContained opts && htmlFormat
425 then makeSelfContained writerOptions
427 handleEntities = if (htmlFormat ||
428 format == "docbook4" ||
429 format == "docbook5" ||
430 format == "docbook") && optAscii opts
433 output <- f writerOptions doc'
434 selfcontain (output ++ ['\n' | not standalone]) >>=
435 writerFn outputFile . handleEntities
437type Transform = Pandoc -> Pandoc
439isTextFormat :: String -> Bool
440isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
442externalFilter :: MonadIO m => FilePath -> [String] -> Pandoc -> m Pandoc
443externalFilter f args' d = liftIO $ do
444 exists <- doesFileExist f
445 isExecutable <- if exists
446 then executable <$> getPermissions f
448 let (f', args'') = if exists
449 then case map toLower (takeExtension f) of
450 _ | isExecutable -> ("." </> f, args')
451 ".py" -> ("python", f:args')
452 ".hs" -> ("runhaskell", f:args')
453 ".pl" -> ("perl", f:args')
454 ".rb" -> ("ruby", f:args')
455 ".php" -> ("php", f:args')
456 ".js" -> ("node", f:args')
459 unless (exists && isExecutable) $ do
460 mbExe <- findExecutable f'
461 when (isNothing mbExe) $
462 err 83 $ "Error running filter " ++ f ++ ":\n" ++
463 "Could not find executable '" ++ f' ++ "'."
464 env <- getEnvironment
465 let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env
466 (exitcode, outbs) <- E.handle filterException $
467 pipeProcess env' f' args'' $ encode d
469 ExitSuccess -> return $ either error id $ eitherDecode' outbs
470 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
471 "Filter returned error status " ++ show ec
472 where filterException :: E.SomeException -> IO a
473 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
476-- | Data structure for command line options.
478 { optTabStop :: Int -- ^ Number of spaces per tab
479 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
480 , optStandalone :: Bool -- ^ Include header, footer
481 , optReader :: Maybe String -- ^ Reader format
482 , optWriter :: Maybe String -- ^ Writer format
483 , optTableOfContents :: Bool -- ^ Include table of contents
484 , optBaseHeaderLevel :: Int -- ^ Base header level
485 , optTemplate :: Maybe FilePath -- ^ Custom template
486 , optVariables :: [(String,String)] -- ^ Template variables to set
487 , optMetadata :: [(String, String)] -- ^ Metadata fields to set
488 , optOutputFile :: FilePath -- ^ Name of output file
489 , optInputFiles :: [FilePath] -- ^ Names of input files
490 , optNumberSections :: Bool -- ^ Number sections in LaTeX
491 , optNumberOffset :: [Int] -- ^ Starting number for sections
492 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
493 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
494 , optSelfContained :: Bool -- ^ Make HTML accessible offline
495 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
496 , optHighlightStyle :: Maybe String -- ^ Style to use for highlighted code
497 , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions
498 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
499 , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file
500 , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc
501 , optEpubMetadata :: Maybe FilePath -- ^ EPUB metadata
502 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
503 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
504 , optEpubCoverImage :: Maybe FilePath -- ^ Cover image for epub
505 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
506 , optDumpArgs :: Bool -- ^ Output command-line arguments
507 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
508 , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output
509 , optLogFile :: Maybe FilePath -- ^ File to write JSON log output
510 , optFailIfWarnings :: Bool -- ^ Fail on warnings
511 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
512 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
513 , optDpi :: Int -- ^ Dpi
514 , optWrapText :: WrapOption -- ^ Options for wrapping text
515 , optColumns :: Int -- ^ Line length in characters
516 , optFilters :: [FilePath] -- ^ Filters to apply
517 , optEmailObfuscation :: ObfuscationMethod
518 , optIdentifierPrefix :: String
519 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
520 , optDataDir :: Maybe FilePath
521 , optCiteMethod :: CiteMethod -- ^ Method to output cites
522 , optListings :: Bool -- ^ Use listings package for code blocks
523 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
524 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
525 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
526 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
527 , optAscii :: Bool -- ^ Use ascii characters only in html
528 , optDefaultImageExtension :: String -- ^ Default image extension
529 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
530 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
531 , optFileScope :: Bool -- ^ Parse input files before combining
532 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
533 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
534 , optTitlePrefix :: Maybe String -- ^ Prefix for title
535 , optCss :: [FilePath] -- ^ CSS files to link to
536 , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before
537 , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body
538 , optIncludeInHeader :: [FilePath] -- ^ Files to include in header
541-- | Defaults for command-line options.
545 , optPreserveTabs = False
546 , optStandalone = False
547 , optReader = Nothing
548 , optWriter = Nothing
549 , optTableOfContents = False
550 , optBaseHeaderLevel = 1
551 , optTemplate = Nothing
554 , optOutputFile = "-" -- "-" means stdout
556 , optNumberSections = False
557 , optNumberOffset = [0,0,0,0,0,0]
558 , optSectionDivs = False
559 , optIncremental = False
560 , optSelfContained = False
561 , optHtmlQTags = False
562 , optHighlightStyle = Just "pygments"
563 , optTopLevelDivision = TopLevelDefault
564 , optHTMLMathMethod = PlainMath
565 , optAbbreviations = Nothing
566 , optReferenceDoc = Nothing
567 , optEpubMetadata = Nothing
569 , optEpubChapterLevel = 1
570 , optEpubCoverImage = Nothing
572 , optDumpArgs = False
573 , optIgnoreArgs = False
574 , optVerbosity = WARNING
575 , optLogFile = Nothing
576 , optFailIfWarnings = False
577 , optReferenceLinks = False
578 , optReferenceLocation = EndOfDocument
580 , optWrapText = WrapAuto
583 , optEmailObfuscation = NoObfuscation
584 , optIdentifierPrefix = ""
585 , optIndentedCodeClasses = []
586 , optDataDir = Nothing
587 , optCiteMethod = Citeproc
588 , optListings = False
589 , optLaTeXEngine = "pdflatex"
590 , optLaTeXEngineArgs = []
591 , optSlideLevel = Nothing
592 , optSetextHeaders = True
594 , optDefaultImageExtension = ""
595 , optExtractMedia = Nothing
596 , optTrackChanges = AcceptChanges
597 , optFileScope = False
598 , optKaTeXStylesheet = Nothing
599 , optKaTeXJS = Nothing
600 , optTitlePrefix = Nothing
602 , optIncludeBeforeBody = []
603 , optIncludeAfterBody = []
604 , optIncludeInHeader = []
607addMetadata :: (String, String) -> Pandoc -> Pandoc
608addMetadata (k, v) (Pandoc meta bs) = Pandoc meta' bs
609 where meta' = case lookupMeta k meta of
610 Nothing -> setMeta k v' meta
611 Just (MetaList xs) ->
612 setMeta k (MetaList (xs ++ [v'])) meta
613 Just x -> setMeta k (MetaList [x, v']) meta
616readMetaValue :: String -> MetaValue
617readMetaValue s = case decode (UTF8.fromString s) of
618 Just (Yaml.String t) -> MetaString $ T.unpack t
619 Just (Yaml.Bool b) -> MetaBool b
622-- Determine default reader based on source file extensions
623defaultReaderName :: String -> [FilePath] -> String
624defaultReaderName fallback [] = fallback
625defaultReaderName fallback (x:xs) =
626 case takeExtension (map toLower x) of
631 ".markdown" -> "markdown"
637 ".lhs" -> "markdown+lhs"
640 ".wiki" -> "mediawiki"
641 ".dokuwiki" -> "dokuwiki"
642 ".textile" -> "textile"
643 ".native" -> "native"
649 ".pdf" -> "pdf" -- so we get an "unknown reader" error
650 ".doc" -> "doc" -- so we get an "unknown reader" error
651 _ -> defaultReaderName fallback xs
653-- Determine default writer based on output file extension
654defaultWriterName :: FilePath -> String
655defaultWriterName "-" = "html" -- no output file
657 case takeExtension (map toLower x) of
658 "" -> "markdown" -- empty extension
662 ".context" -> "context"
667 ".native" -> "native"
670 ".text" -> "markdown"
672 ".markdown" -> "markdown"
673 ".textile" -> "textile"
674 ".lhs" -> "markdown+lhs"
676 ".texinfo" -> "texinfo"
682 ".asciidoc" -> "asciidoc"
683 ".adoc" -> "asciidoc"
690 ['.',y] | y `elem` ['1'..'9'] -> "man"
693-- Transformations of a Pandoc document post-parsing:
695extractMedia :: MonadIO m => MediaBag -> FilePath -> Pandoc -> m Pandoc
696extractMedia media dir d =
697 case [fp | (fp, _, _) <- mediaDirectory media] of
700 extractMediaBag True dir media
701 return $ walk (adjustImagePath dir fps) d
703adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
704adjustImagePath dir paths (Image attr lab (src, tit))
705 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
706adjustImagePath _ _ x = x
708applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc
709applyTransforms transforms d = return $ foldr ($) d transforms
711 -- First we check to see if a filter is found. If not, and if it's
712 -- not an absolute path, we check to see whether it's in `userdir/filters`.
713 -- If not, we leave it unchanged.
714expandFilterPath :: MonadIO m => Maybe FilePath -> FilePath -> m FilePath
715expandFilterPath mbDatadir fp = liftIO $ do
716 fpExists <- doesFileExist fp
719 else case mbDatadir of
720 Just datadir | isRelative fp -> do
721 let filterPath = datadir </> "filters" </> fp
722 filterPathExists <- doesFileExist filterPath
724 then return filterPath
728applyFilters :: MonadIO m
729 => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
730applyFilters mbDatadir filters args d = do
731 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
732 foldrM ($) d $ map (flip externalFilter args) expandedFilters
734readSource :: MonadIO m => FilePath -> m String
735readSource "-" = liftIO UTF8.getContents
736readSource src = case parseURI src of
737 Just u | uriScheme u `elem` ["http:","https:"] ->
739 | uriScheme u == "file:" ->
740 liftIO $ UTF8.readFile (uriPath u)
741 _ -> liftIO $ UTF8.readFile src
743readURI :: MonadIO m => FilePath -> m String
744readURI src = liftIO $ (UTF8.toString . fst) <$> openURL src
746readFile' :: MonadIO m => FilePath -> m B.ByteString
747readFile' "-" = liftIO B.getContents
748readFile' f = liftIO $ B.readFile f
750writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m ()
751writeFnBinary "-" = liftIO . B.putStr
752writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f)
754writerFn :: MonadIO m => FilePath -> String -> m ()
755writerFn "-" = liftIO . UTF8.putStr
756writerFn f = liftIO . UTF8.writeFile f
758lookupHighlightStyle :: Maybe String -> IO (Maybe Style)
759lookupHighlightStyle Nothing = return Nothing
760lookupHighlightStyle (Just s) =
761 case lookup (map toLower s) highlightingStyles of
762 Just sty -> return (Just sty)
763 Nothing -> err 68 $ "Unknown highlight-style " ++ s
765-- | A list of functions, each transforming the options data structure
766-- in response to a command-line option.
767options :: [OptDescr (Opt -> IO Opt)]
769 [ Option "fr" ["from","read"]
771 (\arg opt -> return opt { optReader = Just arg })
775 , Option "tw" ["to","write"]
777 (\arg opt -> return opt { optWriter = Just arg })
781 , Option "o" ["output"]
783 (\arg opt -> return opt { optOutputFile = arg })
785 "" -- "Name of output file"
787 , Option "" ["data-dir"]
789 (\arg opt -> return opt { optDataDir = Just arg })
790 "DIRECTORY") -- "Directory containing pandoc data files."
793 , Option "" ["base-header-level"]
797 Just t | t > 0 && t < 6 ->
798 return opt{ optBaseHeaderLevel = t }
800 "base-header-level must be 1-5")
802 "" -- "Headers base level"
804 , Option "" ["indented-code-classes"]
806 (\arg opt -> return opt { optIndentedCodeClasses = words $
807 map (\c -> if c == ',' then ' ' else c) arg })
809 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
811 , Option "F" ["filter"]
813 (\arg opt -> return opt { optFilters = arg : optFilters opt })
815 "" -- "External JSON filter"
817 , Option "p" ["preserve-tabs"]
819 (\opt -> return opt { optPreserveTabs = True }))
820 "" -- "Preserve tabs instead of converting to spaces"
822 , Option "" ["tab-stop"]
826 Just t | t > 0 -> return opt { optTabStop = t }
828 "tab-stop must be a number greater than 0")
830 "" -- "Tab stop (default 4)"
832 , Option "" ["track-changes"]
835 action <- case arg of
836 "accept" -> return AcceptChanges
837 "reject" -> return RejectChanges
838 "all" -> return AllChanges
840 ("Unknown option for track-changes: " ++ arg)
841 return opt { optTrackChanges = action })
843 "" -- "Accepting or reject MS Word track-changes.""
845 , Option "" ["file-scope"]
847 (\opt -> return opt { optFileScope = True }))
848 "" -- "Parse input files before combining"
850 , Option "" ["extract-media"]
853 return opt { optExtractMedia = Just arg })
855 "" -- "Directory to which to extract embedded media"
857 , Option "s" ["standalone"]
859 (\opt -> return opt { optStandalone = True }))
860 "" -- "Include needed header and footer on output"
862 , Option "" ["template"]
865 return opt{ optTemplate = Just arg,
866 optStandalone = True })
868 "" -- "Use custom template"
870 , Option "M" ["metadata"]
873 let (key, val) = splitField arg
874 return opt{ optMetadata = (key, val) : optMetadata opt })
878 , Option "V" ["variable"]
881 let (key, val) = splitField arg
882 return opt{ optVariables = (key, val) : optVariables opt })
886 , Option "D" ["print-default-template"]
889 templ <- getDefaultTemplate Nothing arg
891 Right t -> UTF8.hPutStr stdout t
892 Left e -> error $ show e
895 "" -- "Print default template for FORMAT"
897 , Option "" ["print-default-data-file"]
900 readDataFile Nothing arg >>= BS.hPutStr stdout
903 "" -- "Print default data file"
909 Just t | t > 0 -> return opt { optDpi = t }
911 "dpi must be a number greater than 0")
913 "" -- "Dpi (default 96)"
918 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
919 Just o -> return opt { optWrapText = o }
920 Nothing -> err 77 "--wrap must be auto, none, or preserve")
921 "auto|none|preserve")
922 "" -- "Option for wrapping text in output"
924 , Option "" ["columns"]
928 Just t | t > 0 -> return opt { optColumns = t }
930 "columns must be a number greater than 0")
932 "" -- "Length of line in characters"
934 , Option "" ["toc", "table-of-contents"]
936 (\opt -> return opt { optTableOfContents = True }))
937 "" -- "Include table of contents"
939 , Option "" ["toc-depth"]
943 Just t | t >= 1 && t <= 6 ->
944 return opt { optTOCDepth = t }
946 "TOC level must be a number between 1 and 6")
948 "" -- "Number of levels to include in TOC"
950 , Option "" ["no-highlight"]
952 (\opt -> return opt { optHighlightStyle = Nothing }))
953 "" -- "Don't highlight source code"
955 , Option "" ["highlight-style"]
957 (\arg opt -> return opt{ optHighlightStyle = Just arg })
959 "" -- "Style for highlighted code"
961 , Option "H" ["include-in-header"]
963 (\arg opt -> return opt{ optIncludeInHeader =
964 arg : optIncludeInHeader opt,
965 optStandalone = True })
967 "" -- "File to include at end of header (implies -s)"
969 , Option "B" ["include-before-body"]
971 (\arg opt -> return opt{ optIncludeBeforeBody =
972 arg : optIncludeBeforeBody opt,
973 optStandalone = True })
975 "" -- "File to include before document body"
977 , Option "A" ["include-after-body"]
979 (\arg opt -> return opt{ optIncludeAfterBody =
980 arg : optIncludeAfterBody opt,
981 optStandalone = True })
983 "" -- "File to include after document body"
985 , Option "" ["self-contained"]
987 (\opt -> return opt { optSelfContained = True,
988 optStandalone = True }))
989 "" -- "Make slide shows include all the needed js and css"
991 , Option "" ["html-q-tags"]
994 return opt { optHtmlQTags = True }))
995 "" -- "Use <q> tags for quotes in HTML"
997 , Option "" ["ascii"]
999 (\opt -> return opt { optAscii = True }))
1000 "" -- "Use ascii characters only in HTML output"
1002 , Option "" ["reference-links"]
1004 (\opt -> return opt { optReferenceLinks = True } ))
1005 "" -- "Use reference links in parsing HTML"
1007 , Option "" ["reference-location"]
1010 action <- case arg of
1011 "block" -> return EndOfBlock
1012 "section" -> return EndOfSection
1013 "document" -> return EndOfDocument
1015 ("Unknown option for reference-location: " ++ arg)
1016 return opt { optReferenceLocation = action })
1017 "block|section|document")
1018 "" -- "Accepting or reject MS Word track-changes.""
1020 , Option "" ["atx-headers"]
1022 (\opt -> return opt { optSetextHeaders = False } ))
1023 "" -- "Use atx-style headers for markdown"
1025 , Option "" ["top-level-division"]
1028 let tldName = "TopLevel" ++ uppercaseFirstLetter arg
1029 case safeRead tldName of
1030 Just tlDiv -> return opt { optTopLevelDivision = tlDiv }
1031 _ -> err 76 ("Top-level division must be " ++
1032 "section, chapter, part, or default"))
1033 "section|chapter|part")
1034 "" -- "Use top-level division type in LaTeX, ConTeXt, DocBook"
1036 , Option "N" ["number-sections"]
1038 (\opt -> return opt { optNumberSections = True }))
1039 "" -- "Number sections in LaTeX"
1041 , Option "" ["number-offset"]
1044 case safeRead ('[':arg ++ "]") of
1045 Just ns -> return opt { optNumberOffset = ns,
1046 optNumberSections = True }
1047 _ -> err 57 "could not parse number-offset")
1049 "" -- "Starting number for sections, subsections, etc."
1051 , Option "" ["listings"]
1053 (\opt -> return opt { optListings = True }))
1054 "" -- "Use listings package for LaTeX code blocks"
1056 , Option "i" ["incremental"]
1058 (\opt -> return opt { optIncremental = True }))
1059 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
1061 , Option "" ["slide-level"]
1064 case safeRead arg of
1065 Just t | t >= 1 && t <= 6 ->
1066 return opt { optSlideLevel = Just t }
1068 "slide level must be a number between 1 and 6")
1070 "" -- "Force header level for slides"
1072 , Option "" ["section-divs"]
1074 (\opt -> return opt { optSectionDivs = True }))
1075 "" -- "Put sections in div tags in HTML"
1077 , Option "" ["default-image-extension"]
1079 (\arg opt -> return opt { optDefaultImageExtension = arg })
1081 "" -- "Default extension for extensionless images"
1083 , Option "" ["email-obfuscation"]
1086 method <- case arg of
1087 "references" -> return ReferenceObfuscation
1088 "javascript" -> return JavascriptObfuscation
1089 "none" -> return NoObfuscation
1091 ("Unknown obfuscation method: " ++ arg)
1092 return opt { optEmailObfuscation = method })
1093 "none|javascript|references")
1094 "" -- "Method for obfuscating email in HTML"
1096 , Option "" ["id-prefix"]
1098 (\arg opt -> return opt { optIdentifierPrefix = arg })
1100 "" -- "Prefix to add to automatically generated HTML identifiers"
1102 , Option "T" ["title-prefix"]
1105 let newvars = ("title-prefix", arg) : optVariables opt
1106 return opt { optVariables = newvars,
1107 optStandalone = True })
1109 "" -- "String to prefix to HTML window title"
1111 , Option "c" ["css"]
1113 (\arg opt -> return opt{ optCss = arg : optCss opt })
1114 -- add new link to end, so it is included in proper order
1116 "" -- "Link to CSS style sheet"
1118 , Option "" ["reference-doc"]
1121 return opt { optReferenceDoc = Just arg })
1123 "" -- "Path of custom reference doc"
1125 , Option "" ["epub-cover-image"]
1128 return opt { optVariables =
1129 ("epub-cover-image", arg) : optVariables opt })
1131 "" -- "Path of epub cover image"
1133 , Option "" ["epub-metadata"]
1135 (\arg opt -> return opt { optEpubMetadata = Just arg })
1137 "" -- "Path of epub metadata file"
1139 , Option "" ["epub-embed-font"]
1142 return opt{ optEpubFonts = arg : optEpubFonts opt })
1144 "" -- "Directory of fonts to embed"
1146 , Option "" ["epub-chapter-level"]
1149 case safeRead arg of
1150 Just t | t >= 1 && t <= 6 ->
1151 return opt { optEpubChapterLevel = t }
1153 "chapter level must be a number between 1 and 6")
1155 "" -- "Header level at which to split chapters in EPUB"
1157 , Option "" ["latex-engine"]
1160 let b = takeBaseName arg
1161 if b `elem` ["pdflatex", "lualatex", "xelatex"]
1162 then return opt { optLaTeXEngine = arg }
1163 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
1165 "" -- "Name of latex program to use in generating PDF"
1167 , Option "" ["latex-engine-opt"]
1170 let oldArgs = optLaTeXEngineArgs opt
1171 return opt { optLaTeXEngineArgs = arg : oldArgs })
1173 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
1175 , Option "" ["bibliography"]
1177 (\arg opt -> return opt{ optMetadata =
1178 ("bibliography", arg) : optMetadata opt })
1185 return opt{ optMetadata =
1186 ("csl", arg) : optMetadata opt })
1190 , Option "" ["citation-abbreviations"]
1193 return opt{ optMetadata =
1194 ("citation-abbreviations", arg): optMetadata opt })
1198 , Option "" ["natbib"]
1200 (\opt -> return opt { optCiteMethod = Natbib }))
1201 "" -- "Use natbib cite commands in LaTeX output"
1203 , Option "" ["biblatex"]
1205 (\opt -> return opt { optCiteMethod = Biblatex }))
1206 "" -- "Use biblatex cite commands in LaTeX output"
1208 , Option "m" ["latexmathml", "asciimathml"]
1211 return opt { optHTMLMathMethod = LaTeXMathML arg })
1213 "" -- "Use LaTeXMathML script in html output"
1215 , Option "" ["mathml"]
1218 return opt { optHTMLMathMethod = MathML }))
1219 "" -- "Use mathml for HTML math"
1221 , Option "" ["mimetex"]
1224 let url' = case arg of
1226 Nothing -> "/cgi-bin/mimetex.cgi?"
1227 return opt { optHTMLMathMethod = WebTeX url' })
1229 "" -- "Use mimetex for HTML math"
1231 , Option "" ["webtex"]
1234 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
1235 return opt { optHTMLMathMethod = WebTeX url' })
1237 "" -- "Use web service for HTML math"
1239 , Option "" ["jsmath"]
1241 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
1243 "" -- "Use jsMath for HTML math"
1245 , Option "" ["mathjax"]
1248 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
1249 return opt { optHTMLMathMethod = MathJax url'})
1251 "" -- "Use MathJax for HTML math"
1252 , Option "" ["katex"]
1257 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
1259 "" -- Use KaTeX for HTML Math
1261 , Option "" ["katex-stylesheet"]
1264 return opt { optKaTeXStylesheet = Just arg })
1266 "" -- Set the KaTeX Stylesheet location
1268 , Option "" ["gladtex"]
1270 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
1271 "" -- "Use gladtex for HTML math"
1273 , Option "" ["abbreviations"]
1275 (\arg opt -> return opt { optAbbreviations = Just arg })
1277 "" -- "Specify file for custom abbreviations"
1279 , Option "" ["trace"]
1281 (\opt -> return opt { optVerbosity = DEBUG }))
1282 "" -- "Turn on diagnostic tracing in readers."
1284 , Option "" ["dump-args"]
1286 (\opt -> return opt { optDumpArgs = True }))
1287 "" -- "Print output filename and arguments to stdout."
1289 , Option "" ["ignore-args"]
1291 (\opt -> return opt { optIgnoreArgs = True }))
1292 "" -- "Ignore command-line arguments."
1294 , Option "" ["verbose"]
1296 (\opt -> return opt { optVerbosity = INFO }))
1297 "" -- "Verbose diagnostic output."
1299 , Option "" ["quiet"]
1301 (\opt -> return opt { optVerbosity = ERROR }))
1302 "" -- "Suppress warnings."
1304 , Option "" ["fail-if-warnings"]
1306 (\opt -> return opt { optFailIfWarnings = True }))
1307 "" -- "Exit with error status if there were warnings."
1311 (\arg opt -> return opt{ optLogFile = Just arg })
1313 "" -- "Log messages in JSON format to this file."
1315 , Option "" ["bash-completion"]
1319 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
1320 let optnames (Option shorts longs _ _) =
1321 map (\c -> ['-',c]) shorts ++
1323 let allopts = unwords (concatMap optnames options)
1324 UTF8.hPutStrLn stdout $ printf tpl allopts
1325 (unwords readers'names)
1326 (unwords writers'names)
1327 (unwords $ map fst highlightingStyles)
1330 "" -- "Print bash completion script"
1332 , Option "" ["list-input-formats"]
1335 mapM_ (UTF8.hPutStrLn stdout) readers'names
1339 , Option "" ["list-output-formats"]
1342 mapM_ (UTF8.hPutStrLn stdout) writers'names
1346 , Option "" ["list-extensions"]
1349 let showExt x = drop 4 (show x) ++
1350 if extensionEnabled x pandocExtensions
1353 mapM_ (UTF8.hPutStrLn stdout . showExt)
1354 ([minBound..maxBound] :: [Extension])
1358 , Option "" ["list-highlight-languages"]
1361 let langs = [ T.unpack (T.toLower (sShortname s))
1362 | s <- M.elems defaultSyntaxMap
1363 , sShortname s `notElem`
1364 [T.pack "Alert", T.pack "Alert_indent"]
1366 mapM_ (UTF8.hPutStrLn stdout) langs
1370 , Option "" ["list-highlight-styles"]
1373 mapM_ (UTF8.hPutStrLn stdout . fst) highlightingStyles
1377 , Option "v" ["version"]
1381 defaultDatadir <- E.catch
1382 (getAppUserDataDirectory "pandoc")
1383 (\e -> let _ = (e :: E.SomeException)
1385 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
1386 compileInfo ++ "\nDefault user data directory: " ++
1387 defaultDatadir ++ copyrightMessage)
1389 "" -- "Print version"
1391 , Option "h" ["help"]
1395 UTF8.hPutStr stdout (usageMessage prg options)
1401-- Returns usage message
1402usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
1403usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]")
1405copyrightMessage :: String
1406copyrightMessage = intercalate "\n" [
1408 "Copyright (C) 2006-2017 John MacFarlane",
1409 "Web: http://pandoc.org",
1410 "This is free software; see the source for copying conditions.",
1411 "There is no warranty, not even for merchantability or fitness",
1412 "for a particular purpose." ]
1414compileInfo :: String
1416 "\nCompiled with pandoc-types " ++ VERSION_pandoc_types ++ ", texmath " ++
1417 VERSION_texmath ++ ", skylighting " ++ VERSION_skylighting
1419handleUnrecognizedOption :: String -> [String] -> [String]
1420handleUnrecognizedOption "--smart" =
1421 (("--smart/-S has been removed. Use +smart or -smart extension instead.\n" ++
1422 "For example: pandoc -f markdown+smart -t markdown-smart.") :)
1423handleUnrecognizedOption "-S" = handleUnrecognizedOption "--smart"
1424handleUnrecognizedOption "--old-dashes" =
1425 ("--old-dashes has been removed. Use +old_dashes extension instead." :)
1426handleUnrecognizedOption "--no-wrap" =
1427 ("--no-wrap has been removed. Use --wrap=none instead." :)
1428handleUnrecognizedOption "--chapters" =
1429 ("--chapters has been removed. Use --top-level-division=chapter instead." :)
1430handleUnrecognizedOption "--reference-docx" =
1431 ("--reference-docx has been removed. Use --reference-doc instead." :)
1432handleUnrecognizedOption "--reference-odt" =
1433 ("--reference-odt has been removed. Use --reference-doc instead." :)
1434handleUnrecognizedOption "--parse-raw" =
1435 ("--parse-raw/-R has been removed. Use +raw_html or +raw_tex extension.\n" :)
1436handleUnrecognizedOption "--epub-stylesheet" =
1437 ("--epub-stylesheet has been removed. Use --css instead.\n" :)
1438handleUnrecognizedOption "-R" = handleUnrecognizedOption "--parse-raw"
1439handleUnrecognizedOption x =
1440 (("Unknown option " ++ x ++ ".") :)
1442uppercaseFirstLetter :: String -> String
1443uppercaseFirstLetter (c:cs) = toUpper c : cs
1444uppercaseFirstLetter [] = []
1446readers'names :: [String]
1447readers'names = sort (map fst (readers :: [(String, Reader PandocIO)]))
1449writers'names :: [String]
1450writers'names = sort (map fst (writers :: [(String, Writer PandocIO)]))
1452splitField :: String -> (String, String)
1454 case break (`elem` ":=") s of
1456 (k,[]) -> (k,"true")
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