1{-# LANGUAGE CPP, TupleSections, ScopedTypeVariables, PatternGuards #-}
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 Skylighting ( defaultSyntaxMap, Syntax(..), Style, tango, pygments,
46 espresso, zenburn, kate, haddock, breezeDark, monochrome )
47import System.Environment ( getArgs, getProgName, getEnvironment )
48import System.Exit ( ExitCode (..), exitSuccess )
50import System.Console.GetOpt
51import qualified Data.Set as Set
52import Data.Char ( toLower, toUpper )
53import Data.List ( intercalate, isPrefixOf, isSuffixOf, sort )
54import System.Directory ( getAppUserDataDirectory, findExecutable,
55 doesFileExist, Permissions(..), getPermissions )
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)
77import System.Posix.Terminal (queryTerminal)
78import System.Posix.IO (stdOutput)
81type Transform = Pandoc -> Pandoc
83copyrightMessage :: String
84copyrightMessage = intercalate "\n" [
86 "Copyright (C) 2006-2016 John MacFarlane",
87 "Web: http://pandoc.org",
88 "This is free software; see the source for copying conditions.",
89 "There is no warranty, not even for merchantability or fitness",
90 "for a particular purpose." ]
94 "\nCompiled with pandoc-types " ++ VERSION_pandoc_types ++ ", texmath " ++
95 VERSION_texmath ++ ", skylighting " ++ VERSION_skylighting
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 exists <- doesFileExist f
119 isExecutable <- if exists
120 then executable <$> getPermissions f
122 let (f', args'') = if exists
123 then case map toLower (takeExtension f) of
124 _ | isExecutable -> ("." </> f, args')
125 ".py" -> ("python", f:args')
126 ".hs" -> ("runhaskell", f:args')
127 ".pl" -> ("perl", f:args')
128 ".rb" -> ("ruby", f:args')
129 ".php" -> ("php", f:args')
130 ".js" -> ("node", f:args')
133 unless (exists && isExecutable) $ do
134 mbExe <- findExecutable f'
135 when (isNothing mbExe) $
136 err 83 $ "Error running filter " ++ f ++ ":\n" ++
137 "Could not find executable '" ++ f' ++ "'."
138 env <- getEnvironment
139 let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env
140 (exitcode, outbs, errbs) <- E.handle filterException $
141 pipeProcess env' f' args'' $ encode d
142 unless (B.null errbs) $ B.hPutStr stderr errbs
144 ExitSuccess -> return $ either error id $ eitherDecode' outbs
145 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
146 "Filter returned error status " ++ show ec
147 where filterException :: E.SomeException -> IO a
148 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
151highlightingStyles :: [(String, Style)]
153 [("pygments", pygments),
155 ("espresso", espresso),
156 ("zenburn", zenburn),
158 ("monochrome", monochrome),
159 ("breezedark", breezeDark),
160 ("haddock", haddock)]
162-- | Data structure for command line options.
164 { optTabStop :: Int -- ^ Number of spaces per tab
165 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
166 , optStandalone :: Bool -- ^ Include header, footer
167 , optReader :: String -- ^ Reader format
168 , optWriter :: String -- ^ Writer format
169 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
170 , optTableOfContents :: Bool -- ^ Include table of contents
171 , optTransforms :: [Transform] -- ^ Doc transforms to apply
172 , optTemplate :: Maybe FilePath -- ^ Custom template
173 , optVariables :: [(String,String)] -- ^ Template variables to set
174 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
175 , optOutputFile :: String -- ^ Name of output file
176 , optNumberSections :: Bool -- ^ Number sections in LaTeX
177 , optNumberOffset :: [Int] -- ^ Starting number for sections
178 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
179 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
180 , optSelfContained :: Bool -- ^ Make HTML accessible offline
181 , optSmart :: Bool -- ^ Use smart typography
182 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
183 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
184 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
185 , optHighlight :: Bool -- ^ Highlight source code
186 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
187 , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions
188 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
189 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
190 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
191 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
192 , optEpubMetadata :: String -- ^ EPUB metadata
193 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
194 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
195 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
196 , optDumpArgs :: Bool -- ^ Output command-line arguments
197 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
198 , optVerbose :: Bool -- ^ Verbose diagnostic output
199 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
200 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
201 , optDpi :: Int -- ^ Dpi
202 , optWrapText :: WrapOption -- ^ Options for wrapping text
203 , optColumns :: Int -- ^ Line length in characters
204 , optFilters :: [FilePath] -- ^ Filters to apply
205 , optEmailObfuscation :: ObfuscationMethod
206 , optIdentifierPrefix :: String
207 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
208 , optDataDir :: Maybe FilePath
209 , optCiteMethod :: CiteMethod -- ^ Method to output cites
210 , optListings :: Bool -- ^ Use listings package for code blocks
211 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
212 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
213 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
214 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
215 , optAscii :: Bool -- ^ Use ascii characters only in html
216 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
217 , optDefaultImageExtension :: String -- ^ Default image extension
218 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
219 , optTrace :: Bool -- ^ Print debug information
220 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
221 , optFileScope :: Bool -- ^ Parse input files before combining
222 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
223 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
226-- | Defaults for command-line options.
230 , optPreserveTabs = False
231 , optStandalone = False
232 , optReader = "" -- null for default reader
233 , optWriter = "" -- null for default writer
234 , optParseRaw = False
235 , optTableOfContents = False
237 , optTemplate = Nothing
239 , optMetadata = M.empty
240 , optOutputFile = "-" -- "-" means stdout
241 , optNumberSections = False
242 , optNumberOffset = [0,0,0,0,0,0]
243 , optSectionDivs = False
244 , optIncremental = False
245 , optSelfContained = False
247 , optOldDashes = False
249 , optHtmlQTags = False
250 , optHighlight = True
251 , optHighlightStyle = pygments
252 , optTopLevelDivision = TopLevelDefault
253 , optHTMLMathMethod = PlainMath
254 , optReferenceODT = Nothing
255 , optReferenceDocx = Nothing
256 , optEpubStylesheet = Nothing
257 , optEpubMetadata = ""
259 , optEpubChapterLevel = 1
261 , optDumpArgs = False
262 , optIgnoreArgs = False
264 , optReferenceLinks = False
265 , optReferenceLocation = EndOfDocument
267 , optWrapText = WrapAuto
270 , optEmailObfuscation = NoObfuscation
271 , optIdentifierPrefix = ""
272 , optIndentedCodeClasses = []
273 , optDataDir = Nothing
274 , optCiteMethod = Citeproc
275 , optListings = False
276 , optLaTeXEngine = "pdflatex"
277 , optLaTeXEngineArgs = []
278 , optSlideLevel = Nothing
279 , optSetextHeaders = True
281 , optTeXLigatures = True
282 , optDefaultImageExtension = ""
283 , optExtractMedia = Nothing
285 , optTrackChanges = AcceptChanges
286 , optFileScope = False
287 , optKaTeXStylesheet = Nothing
288 , optKaTeXJS = Nothing
291-- | A list of functions, each transforming the options data structure
292-- in response to a command-line option.
293options :: [OptDescr (Opt -> IO Opt)]
295 [ Option "fr" ["from","read"]
297 (\arg opt -> return opt { optReader = arg })
301 , Option "tw" ["to","write"]
303 (\arg opt -> return opt { optWriter = arg })
307 , Option "o" ["output"]
309 (\arg opt -> return opt { optOutputFile = arg })
311 "" -- "Name of output file"
313 , Option "" ["data-dir"]
315 (\arg opt -> return opt { optDataDir = Just arg })
316 "DIRECTORY") -- "Directory containing pandoc data files."
319 , Option "R" ["parse-raw"]
321 (\opt -> return opt { optParseRaw = True }))
322 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
324 , Option "S" ["smart"]
326 (\opt -> return opt { optSmart = True }))
327 "" -- "Use smart quotes, dashes, and ellipses"
329 , Option "" ["old-dashes"]
331 (\opt -> return opt { optSmart = True
332 , optOldDashes = True }))
333 "" -- "Use smart quotes, dashes, and ellipses"
335 , Option "" ["base-header-level"]
340 let oldTransforms = optTransforms opt
342 return opt{ optTransforms =
343 headerShift shift : oldTransforms }
345 "base-header-level must be a number > 0")
347 "" -- "Headers base level"
349 , Option "" ["indented-code-classes"]
351 (\arg opt -> return opt { optIndentedCodeClasses = words $
352 map (\c -> if c == ',' then ' ' else c) arg })
354 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
356 , Option "F" ["filter"]
358 (\arg opt -> return opt { optFilters = arg : optFilters opt })
360 "" -- "External JSON filter"
362 , Option "" ["normalize"]
364 (\opt -> return opt { optTransforms =
365 normalize : optTransforms opt } ))
366 "" -- "Normalize the Pandoc AST"
368 , Option "p" ["preserve-tabs"]
370 (\opt -> return opt { optPreserveTabs = True }))
371 "" -- "Preserve tabs instead of converting to spaces"
373 , Option "" ["tab-stop"]
377 Just t | t > 0 -> return opt { optTabStop = t }
379 "tab-stop must be a number greater than 0")
381 "" -- "Tab stop (default 4)"
383 , Option "" ["track-changes"]
386 action <- case arg of
387 "accept" -> return AcceptChanges
388 "reject" -> return RejectChanges
389 "all" -> return AllChanges
391 ("Unknown option for track-changes: " ++ arg)
392 return opt { optTrackChanges = action })
394 "" -- "Accepting or reject MS Word track-changes.""
396 , Option "" ["file-scope"]
398 (\opt -> return opt { optFileScope = True }))
399 "" -- "Parse input files before combining"
401 , Option "" ["extract-media"]
404 return opt { optExtractMedia = Just arg })
406 "" -- "Directory to which to extract embedded media"
408 , Option "s" ["standalone"]
410 (\opt -> return opt { optStandalone = True }))
411 "" -- "Include needed header and footer on output"
413 , Option "" ["template"]
416 return opt{ optTemplate = Just arg,
417 optStandalone = True })
419 "" -- "Use custom template"
421 , Option "M" ["metadata"]
424 let (key,val) = case break (`elem` ":=") arg of
425 (k,_:v) -> (k, readMetaValue v)
426 (k,_) -> (k, MetaBool True)
427 return opt{ optMetadata = addMetadata key val
432 , Option "V" ["variable"]
435 let (key,val) = case break (`elem` ":=") arg of
438 return opt{ optVariables = (key,val) : optVariables opt })
442 , Option "D" ["print-default-template"]
445 templ <- getDefaultTemplate Nothing arg
447 Right t -> UTF8.hPutStr stdout t
448 Left e -> error $ show e
451 "" -- "Print default template for FORMAT"
453 , Option "" ["print-default-data-file"]
456 readDataFile Nothing arg >>= BS.hPutStr stdout
459 "" -- "Print default data file"
465 Just t | t > 0 -> return opt { optDpi = t }
467 "dpi must be a number greater than 0")
469 "" -- "Dpi (default 96)"
471 , Option "" ["no-wrap"]
473 (\opt -> do warn $ "--no-wrap is deprecated. " ++
474 "Use --wrap=none or --wrap=preserve instead."
475 return opt { optWrapText = WrapNone }))
481 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
482 Just o -> return opt { optWrapText = o }
483 Nothing -> err 77 "--wrap must be auto, none, or preserve")
484 "auto|none|preserve")
485 "" -- "Option for wrapping text in output"
487 , Option "" ["columns"]
491 Just t | t > 0 -> return opt { optColumns = t }
493 "columns must be a number greater than 0")
495 "" -- "Length of line in characters"
497 , Option "" ["toc", "table-of-contents"]
499 (\opt -> return opt { optTableOfContents = True }))
500 "" -- "Include table of contents"
502 , Option "" ["toc-depth"]
506 Just t | t >= 1 && t <= 6 ->
507 return opt { optTOCDepth = t }
509 "TOC level must be a number between 1 and 6")
511 "" -- "Number of levels to include in TOC"
513 , Option "" ["no-highlight"]
515 (\opt -> return opt { optHighlight = False }))
516 "" -- "Don't highlight source code"
518 , Option "" ["highlight-style"]
521 case lookup (map toLower arg) highlightingStyles of
522 Just s -> return opt{ optHighlightStyle = s }
523 Nothing -> err 39 $ "Unknown style: " ++ arg)
525 "" -- "Style for highlighted code"
527 , Option "H" ["include-in-header"]
530 text <- UTF8.readFile arg
531 -- add new ones to end, so they're included in order specified
532 let newvars = optVariables opt ++ [("header-includes",text)]
533 return opt { optVariables = newvars,
534 optStandalone = True })
536 "" -- "File to include at end of header (implies -s)"
538 , Option "B" ["include-before-body"]
541 text <- UTF8.readFile arg
542 -- add new ones to end, so they're included in order specified
543 let newvars = optVariables opt ++ [("include-before",text)]
544 return opt { optVariables = newvars,
545 optStandalone = True })
547 "" -- "File to include before document body"
549 , Option "A" ["include-after-body"]
552 text <- UTF8.readFile arg
553 -- add new ones to end, so they're included in order specified
554 let newvars = optVariables opt ++ [("include-after",text)]
555 return opt { optVariables = newvars,
556 optStandalone = True })
558 "" -- "File to include after document body"
560 , Option "" ["self-contained"]
562 (\opt -> return opt { optSelfContained = True,
563 optStandalone = True }))
564 "" -- "Make slide shows include all the needed js and css"
566 , Option "" ["html-q-tags"]
569 return opt { optHtmlQTags = True }))
570 "" -- "Use <q> tags for quotes in HTML"
572 , Option "" ["ascii"]
574 (\opt -> return opt { optAscii = True }))
575 "" -- "Use ascii characters only in HTML output"
577 , Option "" ["reference-links"]
579 (\opt -> return opt { optReferenceLinks = True } ))
580 "" -- "Use reference links in parsing HTML"
582 , Option "" ["reference-location"]
585 action <- case arg of
586 "block" -> return EndOfBlock
587 "section" -> return EndOfSection
588 "document" -> return EndOfDocument
590 ("Unknown option for reference-location: " ++ arg)
591 return opt { optReferenceLocation = action })
592 "block|section|document")
593 "" -- "Accepting or reject MS Word track-changes.""
595 , Option "" ["atx-headers"]
597 (\opt -> return opt { optSetextHeaders = False } ))
598 "" -- "Use atx-style headers for markdown"
600 , Option "" ["chapters"]
602 (\opt -> do warn $ "--chapters is deprecated. " ++
603 "Use --top-level-division=chapter instead."
604 return opt { optTopLevelDivision = TopLevelChapter }))
605 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
607 , Option "" ["top-level-division"]
610 let tldName = "TopLevel" ++ uppercaseFirstLetter arg
611 case safeRead tldName of
612 Just tlDiv -> return opt { optTopLevelDivision = tlDiv }
613 _ -> err 76 ("Top-level division must be " ++
614 "section, chapter, part, or default"))
615 "section|chapter|part")
616 "" -- "Use top-level division type in LaTeX, ConTeXt, DocBook"
618 , Option "N" ["number-sections"]
620 (\opt -> return opt { optNumberSections = True }))
621 "" -- "Number sections in LaTeX"
623 , Option "" ["number-offset"]
626 case safeRead ('[':arg ++ "]") of
627 Just ns -> return opt { optNumberOffset = ns,
628 optNumberSections = True }
629 _ -> err 57 "could not parse number-offset")
631 "" -- "Starting number for sections, subsections, etc."
633 , Option "" ["no-tex-ligatures"]
635 (\opt -> return opt { optTeXLigatures = False }))
636 "" -- "Don't use tex ligatures for quotes, dashes"
638 , Option "" ["listings"]
640 (\opt -> return opt { optListings = True }))
641 "" -- "Use listings package for LaTeX code blocks"
643 , Option "i" ["incremental"]
645 (\opt -> return opt { optIncremental = True }))
646 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
648 , Option "" ["slide-level"]
652 Just t | t >= 1 && t <= 6 ->
653 return opt { optSlideLevel = Just t }
655 "slide level must be a number between 1 and 6")
657 "" -- "Force header level for slides"
659 , Option "" ["section-divs"]
661 (\opt -> return opt { optSectionDivs = True }))
662 "" -- "Put sections in div tags in HTML"
664 , Option "" ["default-image-extension"]
666 (\arg opt -> return opt { optDefaultImageExtension = arg })
668 "" -- "Default extension for extensionless images"
670 , Option "" ["email-obfuscation"]
673 method <- case arg of
674 "references" -> return ReferenceObfuscation
675 "javascript" -> return JavascriptObfuscation
676 "none" -> return NoObfuscation
678 ("Unknown obfuscation method: " ++ arg)
679 return opt { optEmailObfuscation = method })
680 "none|javascript|references")
681 "" -- "Method for obfuscating email in HTML"
683 , Option "" ["id-prefix"]
685 (\arg opt -> return opt { optIdentifierPrefix = arg })
687 "" -- "Prefix to add to automatically generated HTML identifiers"
689 , Option "T" ["title-prefix"]
692 let newvars = ("title-prefix", arg) : optVariables opt
693 return opt { optVariables = newvars,
694 optStandalone = True })
696 "" -- "String to prefix to HTML window title"
701 -- add new link to end, so it is included in proper order
702 let newvars = optVariables opt ++ [("css",arg)]
703 return opt { optVariables = newvars,
704 optStandalone = True })
706 "" -- "Link to CSS style sheet"
708 , Option "" ["reference-odt"]
711 return opt { optReferenceODT = Just arg })
713 "" -- "Path of custom reference.odt"
715 , Option "" ["reference-docx"]
718 return opt { optReferenceDocx = Just arg })
720 "" -- "Path of custom reference.docx"
722 , Option "" ["epub-stylesheet"]
725 text <- UTF8.readFile arg
726 return opt { optEpubStylesheet = Just text })
728 "" -- "Path of epub.css"
730 , Option "" ["epub-cover-image"]
733 return opt { optVariables =
734 ("epub-cover-image", arg) : optVariables opt })
736 "" -- "Path of epub cover image"
738 , Option "" ["epub-metadata"]
741 text <- UTF8.readFile arg
742 return opt { optEpubMetadata = text })
744 "" -- "Path of epub metadata file"
746 , Option "" ["epub-embed-font"]
749 return opt{ optEpubFonts = arg : optEpubFonts opt })
751 "" -- "Directory of fonts to embed"
753 , Option "" ["epub-chapter-level"]
757 Just t | t >= 1 && t <= 6 ->
758 return opt { optEpubChapterLevel = t }
760 "chapter level must be a number between 1 and 6")
762 "" -- "Header level at which to split chapters in EPUB"
764 , Option "" ["latex-engine"]
767 let b = takeBaseName arg
768 if b `elem` ["pdflatex", "lualatex", "xelatex"]
769 then return opt { optLaTeXEngine = arg }
770 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
772 "" -- "Name of latex program to use in generating PDF"
774 , Option "" ["latex-engine-opt"]
777 let oldArgs = optLaTeXEngineArgs opt
778 return opt { optLaTeXEngineArgs = arg : oldArgs })
780 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
782 , Option "" ["bibliography"]
784 (\arg opt -> return opt{ optMetadata = addMetadata
785 "bibliography" (readMetaValue arg)
794 return opt{ optMetadata = addMetadata "csl"
800 , Option "" ["citation-abbreviations"]
803 return opt{ optMetadata = addMetadata
804 "citation-abbreviations"
810 , Option "" ["natbib"]
812 (\opt -> return opt { optCiteMethod = Natbib }))
813 "" -- "Use natbib cite commands in LaTeX output"
815 , Option "" ["biblatex"]
817 (\opt -> return opt { optCiteMethod = Biblatex }))
818 "" -- "Use biblatex cite commands in LaTeX output"
820 , Option "m" ["latexmathml", "asciimathml"]
823 return opt { optHTMLMathMethod = LaTeXMathML arg })
825 "" -- "Use LaTeXMathML script in html output"
827 , Option "" ["mathml"]
830 return opt { optHTMLMathMethod = MathML arg })
832 "" -- "Use mathml for HTML math"
834 , Option "" ["mimetex"]
837 let url' = case arg of
839 Nothing -> "/cgi-bin/mimetex.cgi?"
840 return opt { optHTMLMathMethod = WebTeX url' })
842 "" -- "Use mimetex for HTML math"
844 , Option "" ["webtex"]
847 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
848 return opt { optHTMLMathMethod = WebTeX url' })
850 "" -- "Use web service for HTML math"
852 , Option "" ["jsmath"]
854 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
856 "" -- "Use jsMath for HTML math"
858 , Option "" ["mathjax"]
861 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
862 return opt { optHTMLMathMethod = MathJax url'})
864 "" -- "Use MathJax for HTML math"
865 , Option "" ["katex"]
870 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
872 "" -- Use KaTeX for HTML Math
874 , Option "" ["katex-stylesheet"]
877 return opt { optKaTeXStylesheet = Just arg })
879 "" -- Set the KaTeX Stylesheet location
881 , Option "" ["gladtex"]
883 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
884 "" -- "Use gladtex for HTML math"
886 , Option "" ["trace"]
888 (\opt -> return opt { optTrace = True }))
889 "" -- "Turn on diagnostic tracing in readers."
891 , Option "" ["dump-args"]
893 (\opt -> return opt { optDumpArgs = True }))
894 "" -- "Print output filename and arguments to stdout."
896 , Option "" ["ignore-args"]
898 (\opt -> return opt { optIgnoreArgs = True }))
899 "" -- "Ignore command-line arguments."
901 , Option "" ["verbose"]
903 (\opt -> return opt { optVerbose = True }))
904 "" -- "Verbose diagnostic output."
906 , Option "" ["bash-completion"]
910 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
911 let optnames (Option shorts longs _ _) =
912 map (\c -> ['-',c]) shorts ++
914 let allopts = unwords (concatMap optnames options)
915 UTF8.hPutStrLn stdout $ printf tpl allopts
916 (unwords (map fst readers))
917 (unwords (map fst writers))
918 (unwords $ map fst highlightingStyles)
921 "" -- "Print bash completion script"
923 , Option "" ["list-input-formats"]
926 let readers'names = sort (map fst readers)
927 mapM_ (UTF8.hPutStrLn stdout) readers'names
931 , Option "" ["list-output-formats"]
934 let writers'names = sort (map fst writers)
935 mapM_ (UTF8.hPutStrLn stdout) writers'names
939 , Option "" ["list-extensions"]
942 let showExt x = drop 4 (show x) ++
943 if x `Set.member` pandocExtensions
946 mapM_ (UTF8.hPutStrLn stdout . showExt)
947 ([minBound..maxBound] :: [Extension])
951 , Option "" ["list-highlight-languages"]
954 let langs = [ T.unpack (T.toLower (sShortname s))
955 | s <- M.elems defaultSyntaxMap
956 , sShortname s `notElem`
957 [T.pack "Alert", T.pack "Alert_indent"]
959 mapM_ (UTF8.hPutStrLn stdout) langs
963 , Option "" ["list-highlight-styles"]
966 mapM_ (UTF8.hPutStrLn stdout) $
967 map fst highlightingStyles
971 , Option "v" ["version"]
975 defaultDatadir <- E.catch
976 (getAppUserDataDirectory "pandoc")
977 (\e -> let _ = (e :: E.SomeException)
979 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
980 compileInfo ++ "\nDefault user data directory: " ++
981 defaultDatadir ++ copyrightMessage)
983 "" -- "Print version"
985 , Option "h" ["help"]
989 UTF8.hPutStr stdout (usageMessage prg options)
996addMetadata :: String -> MetaValue -> M.Map String MetaValue
997 -> M.Map String MetaValue
998addMetadata k v m = case M.lookup k m of
999 Nothing -> M.insert k v m
1000 Just (MetaList xs) -> M.insert k
1001 (MetaList (xs ++ [v])) m
1002 Just x -> M.insert k (MetaList [v, x]) m
1004readMetaValue :: String -> MetaValue
1005readMetaValue s = case decode (UTF8.fromString s) of
1006 Just (Yaml.String t) -> MetaString $ T.unpack t
1007 Just (Yaml.Bool b) -> MetaBool b
1010-- Returns usage message
1011usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
1012usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]")
1014-- Determine default reader based on source file extensions
1015defaultReaderName :: String -> [FilePath] -> String
1016defaultReaderName fallback [] = fallback
1017defaultReaderName fallback (x:xs) =
1018 case takeExtension (map toLower x) of
1023 ".markdown" -> "markdown"
1029 ".lhs" -> "markdown+lhs"
1032 ".wiki" -> "mediawiki"
1033 ".dokuwiki" -> "dokuwiki"
1034 ".textile" -> "textile"
1035 ".native" -> "native"
1041 ".pdf" -> "pdf" -- so we get an "unknown reader" error
1042 ".doc" -> "doc" -- so we get an "unknown reader" error
1043 _ -> defaultReaderName fallback xs
1045-- Returns True if extension of first source is .lhs
1046lhsExtension :: [FilePath] -> Bool
1047lhsExtension (x:_) = takeExtension x == ".lhs"
1048lhsExtension _ = False
1050-- Determine default writer based on output file extension
1051defaultWriterName :: FilePath -> String
1052defaultWriterName "-" = "html" -- no output file
1053defaultWriterName x =
1054 case takeExtension (map toLower x) of
1055 "" -> "markdown" -- empty extension
1059 ".context" -> "context"
1064 ".native" -> "native"
1066 ".txt" -> "markdown"
1067 ".text" -> "markdown"
1069 ".markdown" -> "markdown"
1070 ".textile" -> "textile"
1071 ".lhs" -> "markdown+lhs"
1072 ".texi" -> "texinfo"
1073 ".texinfo" -> "texinfo"
1079 ".asciidoc" -> "asciidoc"
1080 ".adoc" -> "asciidoc"
1087 ['.',y] | y `elem` ['1'..'9'] -> "man"
1090-- Transformations of a Pandoc document post-parsing:
1092extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1093extractMedia media dir d =
1094 case [fp | (fp, _, _) <- mediaDirectory media] of
1097 extractMediaBag True dir media
1098 return $ walk (adjustImagePath dir fps) d
1100adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1101adjustImagePath dir paths (Image attr lab (src, tit))
1102 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1103adjustImagePath _ _ x = x
1105adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1106adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1108applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1109applyTransforms transforms d = return $ foldr ($) d transforms
1111 -- First we check to see if a filter is found. If not, and if it's
1112 -- not an absolute path, we check to see whether it's in `userdir/filters`.
1113 -- If not, we leave it unchanged.
1114expandFilterPath :: Maybe FilePath -> FilePath -> IO FilePath
1115expandFilterPath mbDatadir fp = do
1116 fpExists <- doesFileExist fp
1119 else case mbDatadir of
1120 Just datadir | isRelative fp -> do
1121 let filterPath = (datadir </> "filters" </> fp)
1122 filterPathExists <- doesFileExist filterPath
1124 then return filterPath
1128applyFilters :: Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> IO Pandoc
1129applyFilters mbDatadir filters args d = do
1130 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
1131 foldrM ($) d $ map (flip externalFilter args) expandedFilters
1133uppercaseFirstLetter :: String -> String
1134uppercaseFirstLetter (c:cs) = toUpper c : cs
1135uppercaseFirstLetter [] = []
1140 rawArgs <- map UTF8.decodeArg <$> getArgs
1143 let (actions, args, errors) = getOpt Permute options rawArgs
1145 unless (null errors) $
1146 err 2 $ concat $ errors ++
1147 ["Try " ++ prg ++ " --help for more information."]
1149 -- thread option data structure through all supplied option actions
1150 opts <- foldl (>>=) (return defaultOpts) actions
1151 convertWithOpts opts args
1153convertWithOpts :: Opt -> [FilePath] -> IO ()
1154convertWithOpts opts args = do
1155 let Opt { optTabStop = tabStop
1156 , optPreserveTabs = preserveTabs
1157 , optStandalone = standalone
1158 , optReader = readerName
1159 , optWriter = writerName
1160 , optParseRaw = parseRaw
1161 , optVariables = variables
1162 , optMetadata = metadata
1163 , optTableOfContents = toc
1164 , optTransforms = transforms
1165 , optTemplate = templatePath
1166 , optOutputFile = outputFile
1167 , optNumberSections = numberSections
1168 , optNumberOffset = numberFrom
1169 , optSectionDivs = sectionDivs
1170 , optIncremental = incremental
1171 , optSelfContained = selfContained
1173 , optOldDashes = oldDashes
1175 , optHtmlQTags = htmlQTags
1176 , optHighlight = highlight
1177 , optHighlightStyle = highlightStyle
1178 , optTopLevelDivision = topLevelDivision
1179 , optHTMLMathMethod = mathMethod'
1180 , optReferenceODT = referenceODT
1181 , optReferenceDocx = referenceDocx
1182 , optEpubStylesheet = epubStylesheet
1183 , optEpubMetadata = epubMetadata
1184 , optEpubFonts = epubFonts
1185 , optEpubChapterLevel = epubChapterLevel
1186 , optTOCDepth = epubTOCDepth
1187 , optDumpArgs = dumpArgs
1188 , optIgnoreArgs = ignoreArgs
1189 , optVerbose = verbose
1190 , optReferenceLinks = referenceLinks
1191 , optReferenceLocation = referenceLocation
1193 , optWrapText = wrap
1194 , optColumns = columns
1195 , optFilters = filters
1196 , optEmailObfuscation = obfuscationMethod
1197 , optIdentifierPrefix = idPrefix
1198 , optIndentedCodeClasses = codeBlockClasses
1199 , optDataDir = mbDataDir
1200 , optCiteMethod = citeMethod
1201 , optListings = listings
1202 , optLaTeXEngine = latexEngine
1203 , optLaTeXEngineArgs = latexEngineArgs
1204 , optSlideLevel = slideLevel
1205 , optSetextHeaders = setextHeaders
1207 , optTeXLigatures = texLigatures
1208 , optDefaultImageExtension = defaultImageExtension
1209 , optExtractMedia = mbExtractMedia
1211 , optTrackChanges = trackChanges
1212 , optFileScope = fileScope
1213 , optKaTeXStylesheet = katexStylesheet
1214 , optKaTeXJS = katexJS
1218 do UTF8.hPutStrLn stdout outputFile
1219 mapM_ (UTF8.hPutStrLn stdout) args
1222 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
1224 case (katexJS, katexStylesheet) of
1225 (Nothing, _) -> mathMethod'
1226 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1229 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1230 let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1231 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1232 "pandoc-citeproc" `notElem` map takeBaseName filters
1233 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1236 let sources = if ignoreArgs then [] else args
1238 datadir <- case mbDataDir of
1240 (Just <$> getAppUserDataDirectory "pandoc")
1241 (\e -> let _ = (e :: E.SomeException)
1243 Just _ -> return mbDataDir
1245 -- assign reader and writer based on options and filenames
1246 let readerName' = case map toLower readerName of
1247 [] -> defaultReaderName
1248 (if any isURI sources
1250 else "markdown") sources
1254 let writerName' = case map toLower writerName of
1255 [] -> defaultWriterName outputFile
1259 let format = takeWhile (`notElem` ['+','-'])
1260 $ takeFileName writerName' -- in case path to lua script
1262 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1264 let laTeXOutput = format `elem` ["latex", "beamer"]
1265 let conTeXtOutput = format == "context"
1266 let html5Output = format == "html5"
1268 let laTeXInput = "latex" `isPrefixOf` readerName' ||
1269 "beamer" `isPrefixOf` readerName'
1271 writer <- if ".lua" `isSuffixOf` format
1272 -- note: use non-lowercased version writerName
1273 then return $ IOStringWriter $ writeCustom writerName
1274 else case getWriter writerName' of
1278 "\nTo create a pdf with pandoc, use " ++
1279 "the latex or beamer writer and specify\n" ++
1280 "an output file with .pdf extension " ++
1281 "(pandoc -t latex -o filename.pdf)."
1285 reader <- if "t2t" == readerName'
1286 then (mkStringReader .
1288 getT2TMeta sources outputFile
1289 else case getReader readerName' of
1292 where e' = case readerName' of
1294 "\nPandoc can convert to PDF, but not from PDF."
1296 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1299 let standalone' = standalone || not (isTextFormat format) || pdfOutput
1301 templ <- case templatePath of
1302 _ | not standalone' -> return Nothing
1304 deftemp <- getDefaultTemplate datadir format
1307 Right t -> return (Just t)
1309 -- strip off extensions
1310 let tp' = case takeExtension tp of
1313 Just <$> E.catch (UTF8.readFile tp')
1314 (\e -> if isDoesNotExistError e
1316 (readDataFileUTF8 datadir
1317 ("templates" </> tp'))
1318 (\e' -> let _ = (e' :: E.SomeException)
1322 variables' <- case mathMethod of
1323 LaTeXMathML Nothing -> do
1324 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1325 return $ ("mathml-script", s) : variables
1326 _ -> return variables
1328 variables'' <- if format == "dzslides"
1330 dztempl <- readDataFileUTF8 datadir
1331 ("dzslides" </> "template.html")
1332 let dzline = "<!-- {{{{ dzslides core"
1333 let dzcore = unlines
1334 $ dropWhile (not . (dzline `isPrefixOf`))
1336 return $ ("dzslides-core", dzcore) : variables'
1337 else return variables'
1339 let sourceURL = case sources of
1341 (x:_) -> case parseURI x of
1343 | uriScheme u `elem` ["http:","https:"] ->
1344 Just $ show u{ uriQuery = "",
1348 let readerOpts = def{ readerSmart = if laTeXInput
1350 else smart || (texLigatures &&
1351 (laTeXOutput || conTeXtOutput))
1352 , readerStandalone = standalone'
1353 , readerParseRaw = parseRaw
1354 , readerColumns = columns
1355 , readerTabStop = tabStop
1356 , readerOldDashes = oldDashes
1357 , readerIndentedCodeClasses = codeBlockClasses
1358 , readerApplyMacros = not laTeXOutput
1359 , readerDefaultImageExtension = defaultImageExtension
1360 , readerTrace = trace
1361 , readerTrackChanges = trackChanges
1362 , readerFileScope = fileScope
1368 istty <- queryTerminal stdOutput
1370 when (istty && not (isTextFormat format) && outputFile == "-") $
1371 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1372 "Specify an output file using the -o option."
1374 let readSources [] = mapM readSource ["-"]
1375 readSources srcs = mapM readSource srcs
1376 readSource "-" = UTF8.getContents
1377 readSource src = case parseURI src of
1378 Just u | uriScheme u `elem` ["http:","https:"] ->
1380 | uriScheme u == "file:" ->
1381 UTF8.readFile (uriPath u)
1382 _ -> UTF8.readFile src
1387 Right (bs,_) -> return $ UTF8.toString bs
1389 let readFiles [] = error "Cannot read archive from stdin"
1390 readFiles [x] = B.readFile x
1391 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1393 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1397 let handleIncludes' :: String -> IO (Either PandocError String)
1398 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1402 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1403 sourceToDoc sources' = fmap handleError $
1406 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1407 doc <- handleIncludes' srcs
1408 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1409 ByteStringReader r -> readFiles sources' >>= r readerOpts
1411 -- We parse first if (1) fileScope is set, (2), it's a binary
1412 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1413 -- of negatives as opposed to an OR of positives, so we do default
1414 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1416 (doc, media) <- case reader of
1417 (StringReader _) | not fileScope && readerName' /= "json" ->
1419 _ | null sources -> sourceToDoc sources
1420 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1421 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1423 let writerOptions = def { writerTemplate = templ,
1424 writerVariables = variables'',
1425 writerTabStop = tabStop,
1426 writerTableOfContents = toc,
1427 writerHTMLMathMethod = mathMethod,
1428 writerIncremental = incremental,
1429 writerCiteMethod = citeMethod,
1430 writerIgnoreNotes = False,
1431 writerNumberSections = numberSections,
1432 writerNumberOffset = numberFrom,
1433 writerSectionDivs = sectionDivs,
1434 writerReferenceLinks = referenceLinks,
1435 writerReferenceLocation = referenceLocation,
1437 writerWrapText = wrap,
1438 writerColumns = columns,
1439 writerEmailObfuscation = obfuscationMethod,
1440 writerIdentifierPrefix = idPrefix,
1441 writerSourceURL = sourceURL,
1442 writerUserDataDir = datadir,
1443 writerHtml5 = html5,
1444 writerHtmlQTags = htmlQTags,
1445 writerTopLevelDivision = topLevelDivision,
1446 writerListings = listings,
1447 writerBeamer = False,
1448 writerSlideLevel = slideLevel,
1449 writerHighlight = highlight,
1450 writerHighlightStyle = highlightStyle,
1451 writerSetextHeaders = setextHeaders,
1452 writerTeXLigatures = texLigatures,
1453 writerEpubMetadata = epubMetadata,
1454 writerEpubStylesheet = epubStylesheet,
1455 writerEpubFonts = epubFonts,
1456 writerEpubChapterLevel = epubChapterLevel,
1457 writerTOCDepth = epubTOCDepth,
1458 writerReferenceODT = referenceODT,
1459 writerReferenceDocx = referenceDocx,
1460 writerMediaBag = media,
1461 writerVerbose = verbose,
1462 writerLaTeXArgs = latexEngineArgs
1466 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1467 adjustMetadata metadata >=>
1468 applyTransforms transforms >=>
1469 applyFilters datadir filters' [format]) doc
1471 let writeFnBinary :: FilePath -> B.ByteString -> IO ()
1472 writeFnBinary "-" = B.putStr
1473 writeFnBinary f = B.writeFile (UTF8.encodePath f)
1475 let writerFn :: FilePath -> String -> IO ()
1476 writerFn "-" = UTF8.putStr
1477 writerFn f = UTF8.writeFile f
1480 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1481 IOByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
1484 -- make sure writer is latex or beamer or context or html5
1485 unless (laTeXOutput || conTeXtOutput || html5Output) $
1486 err 47 $ "cannot produce pdf output with " ++ format ++
1489 let pdfprog = case () of
1490 _ | conTeXtOutput -> "context"
1491 _ | html5Output -> "wkhtmltopdf"
1493 -- check for pdf creating program
1494 mbPdfProg <- findExecutable pdfprog
1495 when (isNothing mbPdfProg) $
1496 err 41 $ pdfprog ++ " not found. " ++
1497 pdfprog ++ " is needed for pdf output."
1499 res <- makePDF pdfprog f writerOptions doc'
1501 Right pdf -> writeFnBinary outputFile pdf
1503 B.hPutStr stderr err'
1504 B.hPut stderr $ B.pack [10]
1505 err 43 "Error producing PDF"
1506 | otherwise -> selfcontain (f writerOptions doc' ++
1507 ['\n' | not standalone'])
1508 >>= writerFn outputFile . handleEntities
1509 where htmlFormat = format `elem`
1510 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1511 selfcontain = if selfContained && htmlFormat
1512 then makeSelfContained writerOptions
1514 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