git.iko.soy/typeable/pandoc
on 34412cf help sign in
1456 lines 62 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE TupleSections #-}
4{-
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
20-}
22{- |
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>
28 Stability : alpha
29 Portability : portable
31Does a pandoc conversion based on command-line options.
32-}
33module Text.Pandoc.App (
34 convertWithOpts
35 , Opt(..)
36 , defaultOpts
37 , parseOptions
38 , options
39 ) where
40import Control.Applicative ((<|>))
41import qualified Control.Exception as E
42import Control.Exception.Extensible (throwIO)
43import Control.Monad
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)
65import System.FilePath
66import System.IO (stderr, stdout)
67import System.IO.Error (isDoesNotExistError)
68import Text.Pandoc
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)
81import Text.Printf
82#ifndef _WINDOWS
83import System.Posix.IO (stdOutput)
84import System.Posix.Terminal (queryTerminal)
85#endif
87parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt
88parseOptions options' defaults = do
89 rawArgs <- map UTF8.decodeArg <$> getArgs
90 prg <- getProgName
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
115 exitSuccess
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"
122 let mathMethod =
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
133 else filters
135 let sources = case args of
136 [] -> ["-"]
137 xs | optIgnoreArgs opts -> ["-"]
138 | otherwise -> xs
140 datadir <- case optDataDir opts of
141 Nothing -> E.catch
142 (Just <$> getAppUserDataDirectory "pandoc")
143 (\e -> let _ = (e :: E.SomeException)
144 in return Nothing)
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
151 then "html"
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)
172 :: Writer PandocIO)
173 else case getWriter writerName of
174 Left e -> err 9 $
175 if format == "pdf"
176 then e ++
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)."
181 else e
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)
188 Left e -> err 7 e'
189 where e' = case readerName of
190 "pdf" -> e ++
191 "\nPandoc can convert to PDF, but not from PDF."
192 "doc" -> e ++
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."
194 _ -> e
196 let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
198 templ <- case optTemplate opts of
199 _ | not standalone -> return Nothing
200 Nothing -> do
201 deftemp <- getDefaultTemplate datadir format
202 case deftemp of
203 Left e -> throwIO e
204 Right t -> return (Just t)
205 Just tp -> do
206 -- strip off extensions
207 let tp' = case takeExtension tp of
208 "" -> tp <.> format
209 _ -> tp
210 Just <$> E.catch (UTF8.readFile tp')
211 (\e -> if isDoesNotExistError e
212 then E.catch
213 (readDataFileUTF8 datadir
214 ("templates" </> tp'))
215 (\e' -> let _ = (e' :: E.SomeException)
216 in throwIO e')
217 else throwIO e)
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
231 variables <-
232 return (("outputfile", optOutputFile opts) : optVariables opts)
233 >>=
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.
240 >>=
241 withList (addContentsAsVariable "include-before")
242 (optIncludeBeforeBody opts)
243 >>=
244 withList (addContentsAsVariable "include-after")
245 (optIncludeAfterBody opts)
246 >>=
247 withList (addContentsAsVariable "header-includes")
248 (optIncludeInHeader opts)
249 >>=
250 withList (addStringAsVariable "css") (optCss opts)
251 >>=
252 maybe return (addStringAsVariable "title-prefix") (optTitlePrefix opts)
253 >>=
254 maybe return (addStringAsVariable "epub-cover-image")
255 (optEpubCoverImage opts)
256 >>=
257 (\vars -> case mathMethod of
258 LaTeXMathML Nothing -> do
259 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
260 return $ ("mathml-script", s) : vars
261 _ -> return vars)
262 >>=
263 (\vars -> if format == "dzslides"
264 then do
265 dztempl <- readDataFileUTF8 datadir
266 ("dzslides" </> "template.html")
267 let dzline = "<!-- {{{{ dzslides core"
268 let dzcore = unlines
269 $ dropWhile (not . (dzline `isPrefixOf`))
270 $ lines dztempl
271 return $ ("dzslides-core", dzcore) : vars
272 else return vars)
274 let sourceURL = case sources of
275 [] -> Nothing
276 (x:_) -> case parseURI x of
277 Just u
278 | uriScheme u `elem` ["http:","https:"] ->
279 Just $ show u{ uriQuery = "",
280 uriFragment = "" }
281 _ -> Nothing
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
297 }
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
332 }
335#ifdef _WINDOWS
336 let istty = True
337#else
338 istty <- queryTerminal stdOutput
339#endif
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)]
347 | otherwise -> []
349 let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t"
350 then 0
351 else optTabStop opts)
353 readSources :: (Functor m, MonadIO m) => [FilePath] -> m String
354 readSources srcs = convertTabs . intercalate "\n" <$>
355 mapM readSource srcs
357 let runIO' :: PandocIO a -> IO a
358 runIO' f = do
359 (res, reports) <- runIOorExplode $ do
360 setVerbosity verbosity
361 x <- f
362 rs <- getLog
363 return (x, rs)
364 case optLogFile opts of
365 Nothing -> return ()
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."
370 return res
372 let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag)
373 sourceToDoc sources' =
374 case reader of
375 StringReader r
376 | optFileScope opts || readerName == "json" -> do
377 pairs <- mapM
378 (readSource >=> withMediaBag . r readerOpts) sources
379 return (mconcat (map fst pairs), mconcat (map snd pairs))
380 | otherwise ->
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))
387 runIO' $ do
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
394 case writer of
395 -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile
396 ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
397 StringWriter f
398 | pdfOutput -> do
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 ++
402 " writer"
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'
415 case res of
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"
421 | otherwise -> do
422 let htmlFormat = format `elem`
423 ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"]
424 selfcontain = if optSelfContained opts && htmlFormat
425 then makeSelfContained writerOptions
426 else return
427 handleEntities = if (htmlFormat ||
428 format == "docbook4" ||
429 format == "docbook5" ||
430 format == "docbook") && optAscii opts
431 then toEntities
432 else id
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
447 else return True
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')
457 _ -> (f, args')
458 else (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
468 case exitcode of
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" ++
474 show e
476-- | Data structure for command line options.
477data Opt = Opt
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
539 }
541-- | Defaults for command-line options.
542defaultOpts :: Opt
543defaultOpts = Opt
544 { optTabStop = 4
545 , optPreserveTabs = False
546 , optStandalone = False
547 , optReader = Nothing
548 , optWriter = Nothing
549 , optTableOfContents = False
550 , optBaseHeaderLevel = 1
551 , optTemplate = Nothing
552 , optVariables = []
553 , optMetadata = []
554 , optOutputFile = "-" -- "-" means stdout
555 , optInputFiles = []
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
568 , optEpubFonts = []
569 , optEpubChapterLevel = 1
570 , optEpubCoverImage = Nothing
571 , optTOCDepth = 3
572 , optDumpArgs = False
573 , optIgnoreArgs = False
574 , optVerbosity = WARNING
575 , optLogFile = Nothing
576 , optFailIfWarnings = False
577 , optReferenceLinks = False
578 , optReferenceLocation = EndOfDocument
579 , optDpi = 96
580 , optWrapText = WrapAuto
581 , optColumns = 72
582 , optFilters = []
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
593 , optAscii = False
594 , optDefaultImageExtension = ""
595 , optExtractMedia = Nothing
596 , optTrackChanges = AcceptChanges
597 , optFileScope = False
598 , optKaTeXStylesheet = Nothing
599 , optKaTeXJS = Nothing
600 , optTitlePrefix = Nothing
601 , optCss = []
602 , optIncludeBeforeBody = []
603 , optIncludeAfterBody = []
604 , optIncludeInHeader = []
605 }
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
614 v' = readMetaValue v
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
620 _ -> MetaString s
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
627 ".xhtml" -> "html"
628 ".html" -> "html"
629 ".htm" -> "html"
630 ".md" -> "markdown"
631 ".markdown" -> "markdown"
632 ".tex" -> "latex"
633 ".latex" -> "latex"
634 ".ltx" -> "latex"
635 ".rst" -> "rst"
636 ".org" -> "org"
637 ".lhs" -> "markdown+lhs"
638 ".db" -> "docbook"
639 ".opml" -> "opml"
640 ".wiki" -> "mediawiki"
641 ".dokuwiki" -> "dokuwiki"
642 ".textile" -> "textile"
643 ".native" -> "native"
644 ".json" -> "json"
645 ".docx" -> "docx"
646 ".t2t" -> "t2t"
647 ".epub" -> "epub"
648 ".odt" -> "odt"
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
656defaultWriterName x =
657 case takeExtension (map toLower x) of
658 "" -> "markdown" -- empty extension
659 ".tex" -> "latex"
660 ".latex" -> "latex"
661 ".ltx" -> "latex"
662 ".context" -> "context"
663 ".ctx" -> "context"
664 ".rtf" -> "rtf"
665 ".rst" -> "rst"
666 ".s5" -> "s5"
667 ".native" -> "native"
668 ".json" -> "json"
669 ".txt" -> "markdown"
670 ".text" -> "markdown"
671 ".md" -> "markdown"
672 ".markdown" -> "markdown"
673 ".textile" -> "textile"
674 ".lhs" -> "markdown+lhs"
675 ".texi" -> "texinfo"
676 ".texinfo" -> "texinfo"
677 ".db" -> "docbook"
678 ".odt" -> "odt"
679 ".docx" -> "docx"
680 ".epub" -> "epub"
681 ".org" -> "org"
682 ".asciidoc" -> "asciidoc"
683 ".adoc" -> "asciidoc"
684 ".pdf" -> "latex"
685 ".fb2" -> "fb2"
686 ".opml" -> "opml"
687 ".icml" -> "icml"
688 ".tei.xml" -> "tei"
689 ".tei" -> "tei"
690 ['.',y] | y `elem` ['1'..'9'] -> "man"
691 _ -> "html"
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
698 [] -> return d
699 fps -> do
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
717 if fpExists
718 then return fp
719 else case mbDatadir of
720 Just datadir | isRelative fp -> do
721 let filterPath = datadir </> "filters" </> fp
722 filterPathExists <- doesFileExist filterPath
723 if filterPathExists
724 then return filterPath
725 else return fp
726 _ -> return fp
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:"] ->
738 readURI src
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)]
768options =
769 [ Option "fr" ["from","read"]
770 (ReqArg
771 (\arg opt -> return opt { optReader = Just arg })
772 "FORMAT")
773 ""
775 , Option "tw" ["to","write"]
776 (ReqArg
777 (\arg opt -> return opt { optWriter = Just arg })
778 "FORMAT")
779 ""
781 , Option "o" ["output"]
782 (ReqArg
783 (\arg opt -> return opt { optOutputFile = arg })
784 "FILE")
785 "" -- "Name of output file"
787 , Option "" ["data-dir"]
788 (ReqArg
789 (\arg opt -> return opt { optDataDir = Just arg })
790 "DIRECTORY") -- "Directory containing pandoc data files."
791 ""
793 , Option "" ["base-header-level"]
794 (ReqArg
795 (\arg opt ->
796 case safeRead arg of
797 Just t | t > 0 && t < 6 ->
798 return opt{ optBaseHeaderLevel = t }
799 _ -> err 19
800 "base-header-level must be 1-5")
801 "NUMBER")
802 "" -- "Headers base level"
804 , Option "" ["indented-code-classes"]
805 (ReqArg
806 (\arg opt -> return opt { optIndentedCodeClasses = words $
807 map (\c -> if c == ',' then ' ' else c) arg })
808 "STRING")
809 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
811 , Option "F" ["filter"]
812 (ReqArg
813 (\arg opt -> return opt { optFilters = arg : optFilters opt })
814 "PROGRAM")
815 "" -- "External JSON filter"
817 , Option "p" ["preserve-tabs"]
818 (NoArg
819 (\opt -> return opt { optPreserveTabs = True }))
820 "" -- "Preserve tabs instead of converting to spaces"
822 , Option "" ["tab-stop"]
823 (ReqArg
824 (\arg opt ->
825 case safeRead arg of
826 Just t | t > 0 -> return opt { optTabStop = t }
827 _ -> err 31
828 "tab-stop must be a number greater than 0")
829 "NUMBER")
830 "" -- "Tab stop (default 4)"
832 , Option "" ["track-changes"]
833 (ReqArg
834 (\arg opt -> do
835 action <- case arg of
836 "accept" -> return AcceptChanges
837 "reject" -> return RejectChanges
838 "all" -> return AllChanges
839 _ -> err 6
840 ("Unknown option for track-changes: " ++ arg)
841 return opt { optTrackChanges = action })
842 "accept|reject|all")
843 "" -- "Accepting or reject MS Word track-changes.""
845 , Option "" ["file-scope"]
846 (NoArg
847 (\opt -> return opt { optFileScope = True }))
848 "" -- "Parse input files before combining"
850 , Option "" ["extract-media"]
851 (ReqArg
852 (\arg opt ->
853 return opt { optExtractMedia = Just arg })
854 "PATH")
855 "" -- "Directory to which to extract embedded media"
857 , Option "s" ["standalone"]
858 (NoArg
859 (\opt -> return opt { optStandalone = True }))
860 "" -- "Include needed header and footer on output"
862 , Option "" ["template"]
863 (ReqArg
864 (\arg opt ->
865 return opt{ optTemplate = Just arg,
866 optStandalone = True })
867 "FILE")
868 "" -- "Use custom template"
870 , Option "M" ["metadata"]
871 (ReqArg
872 (\arg opt -> do
873 let (key, val) = splitField arg
874 return opt{ optMetadata = (key, val) : optMetadata opt })
875 "KEY[:VALUE]")
876 ""
878 , Option "V" ["variable"]
879 (ReqArg
880 (\arg opt -> do
881 let (key, val) = splitField arg
882 return opt{ optVariables = (key, val) : optVariables opt })
883 "KEY[:VALUE]")
884 ""
886 , Option "D" ["print-default-template"]
887 (ReqArg
888 (\arg _ -> do
889 templ <- getDefaultTemplate Nothing arg
890 case templ of
891 Right t -> UTF8.hPutStr stdout t
892 Left e -> error $ show e
893 exitSuccess)
894 "FORMAT")
895 "" -- "Print default template for FORMAT"
897 , Option "" ["print-default-data-file"]
898 (ReqArg
899 (\arg _ -> do
900 readDataFile Nothing arg >>= BS.hPutStr stdout
901 exitSuccess)
902 "FILE")
903 "" -- "Print default data file"
905 , Option "" ["dpi"]
906 (ReqArg
907 (\arg opt ->
908 case safeRead arg of
909 Just t | t > 0 -> return opt { optDpi = t }
910 _ -> err 31
911 "dpi must be a number greater than 0")
912 "NUMBER")
913 "" -- "Dpi (default 96)"
915 , Option "" ["wrap"]
916 (ReqArg
917 (\arg opt ->
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"]
925 (ReqArg
926 (\arg opt ->
927 case safeRead arg of
928 Just t | t > 0 -> return opt { optColumns = t }
929 _ -> err 33
930 "columns must be a number greater than 0")
931 "NUMBER")
932 "" -- "Length of line in characters"
934 , Option "" ["toc", "table-of-contents"]
935 (NoArg
936 (\opt -> return opt { optTableOfContents = True }))
937 "" -- "Include table of contents"
939 , Option "" ["toc-depth"]
940 (ReqArg
941 (\arg opt ->
942 case safeRead arg of
943 Just t | t >= 1 && t <= 6 ->
944 return opt { optTOCDepth = t }
945 _ -> err 57
946 "TOC level must be a number between 1 and 6")
947 "NUMBER")
948 "" -- "Number of levels to include in TOC"
950 , Option "" ["no-highlight"]
951 (NoArg
952 (\opt -> return opt { optHighlightStyle = Nothing }))
953 "" -- "Don't highlight source code"
955 , Option "" ["highlight-style"]
956 (ReqArg
957 (\arg opt -> return opt{ optHighlightStyle = Just arg })
958 "STYLE")
959 "" -- "Style for highlighted code"
961 , Option "H" ["include-in-header"]
962 (ReqArg
963 (\arg opt -> return opt{ optIncludeInHeader =
964 arg : optIncludeInHeader opt,
965 optStandalone = True })
966 "FILE")
967 "" -- "File to include at end of header (implies -s)"
969 , Option "B" ["include-before-body"]
970 (ReqArg
971 (\arg opt -> return opt{ optIncludeBeforeBody =
972 arg : optIncludeBeforeBody opt,
973 optStandalone = True })
974 "FILE")
975 "" -- "File to include before document body"
977 , Option "A" ["include-after-body"]
978 (ReqArg
979 (\arg opt -> return opt{ optIncludeAfterBody =
980 arg : optIncludeAfterBody opt,
981 optStandalone = True })
982 "FILE")
983 "" -- "File to include after document body"
985 , Option "" ["self-contained"]
986 (NoArg
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"]
992 (NoArg
993 (\opt ->
994 return opt { optHtmlQTags = True }))
995 "" -- "Use <q> tags for quotes in HTML"
997 , Option "" ["ascii"]
998 (NoArg
999 (\opt -> return opt { optAscii = True }))
1000 "" -- "Use ascii characters only in HTML output"
1002 , Option "" ["reference-links"]
1003 (NoArg
1004 (\opt -> return opt { optReferenceLinks = True } ))
1005 "" -- "Use reference links in parsing HTML"
1007 , Option "" ["reference-location"]
1008 (ReqArg
1009 (\arg opt -> do
1010 action <- case arg of
1011 "block" -> return EndOfBlock
1012 "section" -> return EndOfSection
1013 "document" -> return EndOfDocument
1014 _ -> err 6
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"]
1021 (NoArg
1022 (\opt -> return opt { optSetextHeaders = False } ))
1023 "" -- "Use atx-style headers for markdown"
1025 , Option "" ["top-level-division"]
1026 (ReqArg
1027 (\arg opt -> do
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"]
1037 (NoArg
1038 (\opt -> return opt { optNumberSections = True }))
1039 "" -- "Number sections in LaTeX"
1041 , Option "" ["number-offset"]
1042 (ReqArg
1043 (\arg opt ->
1044 case safeRead ('[':arg ++ "]") of
1045 Just ns -> return opt { optNumberOffset = ns,
1046 optNumberSections = True }
1047 _ -> err 57 "could not parse number-offset")
1048 "NUMBERS")
1049 "" -- "Starting number for sections, subsections, etc."
1051 , Option "" ["listings"]
1052 (NoArg
1053 (\opt -> return opt { optListings = True }))
1054 "" -- "Use listings package for LaTeX code blocks"
1056 , Option "i" ["incremental"]
1057 (NoArg
1058 (\opt -> return opt { optIncremental = True }))
1059 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
1061 , Option "" ["slide-level"]
1062 (ReqArg
1063 (\arg opt ->
1064 case safeRead arg of
1065 Just t | t >= 1 && t <= 6 ->
1066 return opt { optSlideLevel = Just t }
1067 _ -> err 39
1068 "slide level must be a number between 1 and 6")
1069 "NUMBER")
1070 "" -- "Force header level for slides"
1072 , Option "" ["section-divs"]
1073 (NoArg
1074 (\opt -> return opt { optSectionDivs = True }))
1075 "" -- "Put sections in div tags in HTML"
1077 , Option "" ["default-image-extension"]
1078 (ReqArg
1079 (\arg opt -> return opt { optDefaultImageExtension = arg })
1080 "extension")
1081 "" -- "Default extension for extensionless images"
1083 , Option "" ["email-obfuscation"]
1084 (ReqArg
1085 (\arg opt -> do
1086 method <- case arg of
1087 "references" -> return ReferenceObfuscation
1088 "javascript" -> return JavascriptObfuscation
1089 "none" -> return NoObfuscation
1090 _ -> err 6
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"]
1097 (ReqArg
1098 (\arg opt -> return opt { optIdentifierPrefix = arg })
1099 "STRING")
1100 "" -- "Prefix to add to automatically generated HTML identifiers"
1102 , Option "T" ["title-prefix"]
1103 (ReqArg
1104 (\arg opt -> do
1105 let newvars = ("title-prefix", arg) : optVariables opt
1106 return opt { optVariables = newvars,
1107 optStandalone = True })
1108 "STRING")
1109 "" -- "String to prefix to HTML window title"
1111 , Option "c" ["css"]
1112 (ReqArg
1113 (\arg opt -> return opt{ optCss = arg : optCss opt })
1114 -- add new link to end, so it is included in proper order
1115 "URL")
1116 "" -- "Link to CSS style sheet"
1118 , Option "" ["reference-doc"]
1119 (ReqArg
1120 (\arg opt ->
1121 return opt { optReferenceDoc = Just arg })
1122 "FILE")
1123 "" -- "Path of custom reference doc"
1125 , Option "" ["epub-cover-image"]
1126 (ReqArg
1127 (\arg opt ->
1128 return opt { optVariables =
1129 ("epub-cover-image", arg) : optVariables opt })
1130 "FILE")
1131 "" -- "Path of epub cover image"
1133 , Option "" ["epub-metadata"]
1134 (ReqArg
1135 (\arg opt -> return opt { optEpubMetadata = Just arg })
1136 "FILE")
1137 "" -- "Path of epub metadata file"
1139 , Option "" ["epub-embed-font"]
1140 (ReqArg
1141 (\arg opt ->
1142 return opt{ optEpubFonts = arg : optEpubFonts opt })
1143 "FILE")
1144 "" -- "Directory of fonts to embed"
1146 , Option "" ["epub-chapter-level"]
1147 (ReqArg
1148 (\arg opt ->
1149 case safeRead arg of
1150 Just t | t >= 1 && t <= 6 ->
1151 return opt { optEpubChapterLevel = t }
1152 _ -> err 59
1153 "chapter level must be a number between 1 and 6")
1154 "NUMBER")
1155 "" -- "Header level at which to split chapters in EPUB"
1157 , Option "" ["latex-engine"]
1158 (ReqArg
1159 (\arg opt -> do
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.")
1164 "PROGRAM")
1165 "" -- "Name of latex program to use in generating PDF"
1167 , Option "" ["latex-engine-opt"]
1168 (ReqArg
1169 (\arg opt -> do
1170 let oldArgs = optLaTeXEngineArgs opt
1171 return opt { optLaTeXEngineArgs = arg : oldArgs })
1172 "STRING")
1173 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
1175 , Option "" ["bibliography"]
1176 (ReqArg
1177 (\arg opt -> return opt{ optMetadata =
1178 ("bibliography", arg) : optMetadata opt })
1179 "FILE")
1180 ""
1182 , Option "" ["csl"]
1183 (ReqArg
1184 (\arg opt ->
1185 return opt{ optMetadata =
1186 ("csl", arg) : optMetadata opt })
1187 "FILE")
1188 ""
1190 , Option "" ["citation-abbreviations"]
1191 (ReqArg
1192 (\arg opt ->
1193 return opt{ optMetadata =
1194 ("citation-abbreviations", arg): optMetadata opt })
1195 "FILE")
1196 ""
1198 , Option "" ["natbib"]
1199 (NoArg
1200 (\opt -> return opt { optCiteMethod = Natbib }))
1201 "" -- "Use natbib cite commands in LaTeX output"
1203 , Option "" ["biblatex"]
1204 (NoArg
1205 (\opt -> return opt { optCiteMethod = Biblatex }))
1206 "" -- "Use biblatex cite commands in LaTeX output"
1208 , Option "m" ["latexmathml", "asciimathml"]
1209 (OptArg
1210 (\arg opt ->
1211 return opt { optHTMLMathMethod = LaTeXMathML arg })
1212 "URL")
1213 "" -- "Use LaTeXMathML script in html output"
1215 , Option "" ["mathml"]
1216 (NoArg
1217 (\opt ->
1218 return opt { optHTMLMathMethod = MathML }))
1219 "" -- "Use mathml for HTML math"
1221 , Option "" ["mimetex"]
1222 (OptArg
1223 (\arg opt -> do
1224 let url' = case arg of
1225 Just u -> u ++ "?"
1226 Nothing -> "/cgi-bin/mimetex.cgi?"
1227 return opt { optHTMLMathMethod = WebTeX url' })
1228 "URL")
1229 "" -- "Use mimetex for HTML math"
1231 , Option "" ["webtex"]
1232 (OptArg
1233 (\arg opt -> do
1234 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
1235 return opt { optHTMLMathMethod = WebTeX url' })
1236 "URL")
1237 "" -- "Use web service for HTML math"
1239 , Option "" ["jsmath"]
1240 (OptArg
1241 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
1242 "URL")
1243 "" -- "Use jsMath for HTML math"
1245 , Option "" ["mathjax"]
1246 (OptArg
1247 (\arg opt -> do
1248 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
1249 return opt { optHTMLMathMethod = MathJax url'})
1250 "URL")
1251 "" -- "Use MathJax for HTML math"
1252 , Option "" ["katex"]
1253 (OptArg
1254 (\arg opt ->
1255 return opt
1256 { optKaTeXJS =
1257 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
1258 "URL")
1259 "" -- Use KaTeX for HTML Math
1261 , Option "" ["katex-stylesheet"]
1262 (ReqArg
1263 (\arg opt ->
1264 return opt { optKaTeXStylesheet = Just arg })
1265 "URL")
1266 "" -- Set the KaTeX Stylesheet location
1268 , Option "" ["gladtex"]
1269 (NoArg
1270 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
1271 "" -- "Use gladtex for HTML math"
1273 , Option "" ["abbreviations"]
1274 (ReqArg
1275 (\arg opt -> return opt { optAbbreviations = Just arg })
1276 "FILE")
1277 "" -- "Specify file for custom abbreviations"
1279 , Option "" ["trace"]
1280 (NoArg
1281 (\opt -> return opt { optVerbosity = DEBUG }))
1282 "" -- "Turn on diagnostic tracing in readers."
1284 , Option "" ["dump-args"]
1285 (NoArg
1286 (\opt -> return opt { optDumpArgs = True }))
1287 "" -- "Print output filename and arguments to stdout."
1289 , Option "" ["ignore-args"]
1290 (NoArg
1291 (\opt -> return opt { optIgnoreArgs = True }))
1292 "" -- "Ignore command-line arguments."
1294 , Option "" ["verbose"]
1295 (NoArg
1296 (\opt -> return opt { optVerbosity = INFO }))
1297 "" -- "Verbose diagnostic output."
1299 , Option "" ["quiet"]
1300 (NoArg
1301 (\opt -> return opt { optVerbosity = ERROR }))
1302 "" -- "Suppress warnings."
1304 , Option "" ["fail-if-warnings"]
1305 (NoArg
1306 (\opt -> return opt { optFailIfWarnings = True }))
1307 "" -- "Exit with error status if there were warnings."
1309 , Option "" ["log"]
1310 (ReqArg
1311 (\arg opt -> return opt{ optLogFile = Just arg })
1312 "FILE")
1313 "" -- "Log messages in JSON format to this file."
1315 , Option "" ["bash-completion"]
1316 (NoArg
1317 (\_ -> do
1318 ddir <- getDataDir
1319 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
1320 let optnames (Option shorts longs _ _) =
1321 map (\c -> ['-',c]) shorts ++
1322 map ("--" ++) longs
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)
1328 ddir
1329 exitSuccess ))
1330 "" -- "Print bash completion script"
1332 , Option "" ["list-input-formats"]
1333 (NoArg
1334 (\_ -> do
1335 mapM_ (UTF8.hPutStrLn stdout) readers'names
1336 exitSuccess ))
1337 ""
1339 , Option "" ["list-output-formats"]
1340 (NoArg
1341 (\_ -> do
1342 mapM_ (UTF8.hPutStrLn stdout) writers'names
1343 exitSuccess ))
1344 ""
1346 , Option "" ["list-extensions"]
1347 (NoArg
1348 (\_ -> do
1349 let showExt x = drop 4 (show x) ++
1350 if extensionEnabled x pandocExtensions
1351 then " +"
1352 else " -"
1353 mapM_ (UTF8.hPutStrLn stdout . showExt)
1354 ([minBound..maxBound] :: [Extension])
1355 exitSuccess ))
1356 ""
1358 , Option "" ["list-highlight-languages"]
1359 (NoArg
1360 (\_ -> do
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
1367 exitSuccess ))
1368 ""
1370 , Option "" ["list-highlight-styles"]
1371 (NoArg
1372 (\_ -> do
1373 mapM_ (UTF8.hPutStrLn stdout . fst) highlightingStyles
1374 exitSuccess ))
1375 ""
1377 , Option "v" ["version"]
1378 (NoArg
1379 (\_ -> do
1380 prg <- getProgName
1381 defaultDatadir <- E.catch
1382 (getAppUserDataDirectory "pandoc")
1383 (\e -> let _ = (e :: E.SomeException)
1384 in return "")
1385 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
1386 compileInfo ++ "\nDefault user data directory: " ++
1387 defaultDatadir ++ copyrightMessage)
1388 exitSuccess ))
1389 "" -- "Print version"
1391 , Option "h" ["help"]
1392 (NoArg
1393 (\_ -> do
1394 prg <- getProgName
1395 UTF8.hPutStr stdout (usageMessage prg options)
1396 exitSuccess ))
1397 "" -- "Show help"
1401-- Returns usage message
1402usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
1403usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]")
1405copyrightMessage :: String
1406copyrightMessage = intercalate "\n" [
1407 "",
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
1415compileInfo =
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)
1453splitField s =
1454 case break (`elem` ":=") s of
1455 (k,_:v) -> (k,v)
1456 (k,[]) -> (k,"true")