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 Text.Highlighting.Kate ( languages, Style, tango, pygments,
46 espresso, zenburn, kate, haddock, monochrome )
47import System.Environment ( getArgs, getProgName )
48import System.Exit ( ExitCode (..), exitSuccess )
50import System.Console.GetOpt
51import 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 ++ ", highlighting-kate " ++ VERSION_highlighting_kate
97-- | Converts a list of strings into a single string with the items printed as
98-- comma separated words in lines with a maximum line length.
99wrapWords :: Int -> Int -> [String] -> String
100wrapWords indent c = wrap' (c - indent) (c - indent)
103 wrap' cols remaining (x:xs)
104 | remaining == cols =
105 x ++ wrap' cols (remaining - length x) xs
106 | (length x + 1) > remaining =
107 ",\n" ++ replicate indent ' ' ++ x ++
108 wrap' cols (cols - length x) xs
111 wrap' cols (remaining - length x - 2) xs
113isTextFormat :: String -> Bool
114isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
116externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
117externalFilter f args' d = do
118 mbexe <- if '/' `elem` f
119 -- don't check PATH if filter name has a path
121 -- we catch isDoesNotExistError because this will
122 -- be triggered if PATH not set:
123 else E.catch (findExecutable f)
124 (\e -> if isDoesNotExistError e
127 (f', args'') <- case mbexe of
128 Just x -> return (x, args')
130 exists <- doesFileExist f
133 isExecutable <- executable `fmap`
136 case map toLower $ takeExtension f of
137 _ | isExecutable -> (f, args')
138 ".py" -> ("python", f:args')
139 ".hs" -> ("runhaskell", f:args')
140 ".pl" -> ("perl", f:args')
141 ".rb" -> ("ruby", f:args')
142 ".php" -> ("php", f:args')
143 ".js" -> ("node", f:args')
145 else err 85 $ "Filter " ++ f ++ " not found"
147 mbExe <- findExecutable f'
148 when (isNothing mbExe) $
149 err 83 $ "Error running filter " ++ f ++ "\n" ++
150 show f' ++ " not found in path."
151 (exitcode, outbs, errbs) <- E.handle filterException $
152 pipeProcess Nothing f' args'' $ encode d
153 unless (B.null errbs) $ B.hPutStr stderr errbs
155 ExitSuccess -> return $ either error id $ eitherDecode' outbs
156 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
157 "Filter returned error status " ++ show ec
158 where filterException :: E.SomeException -> IO a
159 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
162highlightingStyles :: [(String, Style)]
164 [("pygments", pygments),
166 ("espresso", espresso),
167 ("zenburn", zenburn),
169 ("monochrome", monochrome),
170 ("haddock", haddock)]
172-- | Data structure for command line options.
174 { optTabStop :: Int -- ^ Number of spaces per tab
175 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
176 , optStandalone :: Bool -- ^ Include header, footer
177 , optReader :: String -- ^ Reader format
178 , optWriter :: String -- ^ Writer format
179 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
180 , optTableOfContents :: Bool -- ^ Include table of contents
181 , optTransforms :: [Transform] -- ^ Doc transforms to apply
182 , optTemplate :: Maybe FilePath -- ^ Custom template
183 , optVariables :: [(String,String)] -- ^ Template variables to set
184 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
185 , optOutputFile :: String -- ^ Name of output file
186 , optNumberSections :: Bool -- ^ Number sections in LaTeX
187 , optNumberOffset :: [Int] -- ^ Starting number for sections
188 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
189 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
190 , optSelfContained :: Bool -- ^ Make HTML accessible offline
191 , optSmart :: Bool -- ^ Use smart typography
192 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
193 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
194 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
195 , optHighlight :: Bool -- ^ Highlight source code
196 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
197 , optTopLevelDivision :: Division -- ^ Type of the top-level divisions
198 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
199 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
200 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
201 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
202 , optEpubMetadata :: String -- ^ EPUB metadata
203 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
204 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
205 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
206 , optDumpArgs :: Bool -- ^ Output command-line arguments
207 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
208 , optVerbose :: Bool -- ^ Verbose diagnostic output
209 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
210 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
211 , optDpi :: Int -- ^ Dpi
212 , optWrapText :: WrapOption -- ^ Options for wrapping text
213 , optColumns :: Int -- ^ Line length in characters
214 , optFilters :: [FilePath] -- ^ Filters to apply
215 , optEmailObfuscation :: ObfuscationMethod
216 , optIdentifierPrefix :: String
217 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
218 , optDataDir :: Maybe FilePath
219 , optCiteMethod :: CiteMethod -- ^ Method to output cites
220 , optListings :: Bool -- ^ Use listings package for code blocks
221 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
222 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
223 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
224 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
225 , optAscii :: Bool -- ^ Use ascii characters only in html
226 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
227 , optDefaultImageExtension :: String -- ^ Default image extension
228 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
229 , optTrace :: Bool -- ^ Print debug information
230 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
231 , optFileScope :: Bool -- ^ Parse input files before combining
232 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
233 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
236-- | Defaults for command-line options.
240 , optPreserveTabs = False
241 , optStandalone = False
242 , optReader = "" -- null for default reader
243 , optWriter = "" -- null for default writer
244 , optParseRaw = False
245 , optTableOfContents = False
247 , optTemplate = Nothing
249 , optMetadata = M.empty
250 , optOutputFile = "-" -- "-" means stdout
251 , optNumberSections = False
252 , optNumberOffset = [0,0,0,0,0,0]
253 , optSectionDivs = False
254 , optIncremental = False
255 , optSelfContained = False
257 , optOldDashes = False
259 , optHtmlQTags = False
260 , optHighlight = True
261 , optHighlightStyle = pygments
262 , optTopLevelDivision = Section
263 , optHTMLMathMethod = PlainMath
264 , optReferenceODT = Nothing
265 , optReferenceDocx = Nothing
266 , optEpubStylesheet = Nothing
267 , optEpubMetadata = ""
269 , optEpubChapterLevel = 1
271 , optDumpArgs = False
272 , optIgnoreArgs = False
274 , optReferenceLinks = False
275 , optReferenceLocation = EndOfDocument
277 , optWrapText = WrapAuto
280 , optEmailObfuscation = NoObfuscation
281 , optIdentifierPrefix = ""
282 , optIndentedCodeClasses = []
283 , optDataDir = Nothing
284 , optCiteMethod = Citeproc
285 , optListings = False
286 , optLaTeXEngine = "pdflatex"
287 , optLaTeXEngineArgs = []
288 , optSlideLevel = Nothing
289 , optSetextHeaders = True
291 , optTeXLigatures = True
292 , optDefaultImageExtension = ""
293 , optExtractMedia = Nothing
295 , optTrackChanges = AcceptChanges
296 , optFileScope = False
297 , optKaTeXStylesheet = Nothing
298 , optKaTeXJS = Nothing
301-- | A list of functions, each transforming the options data structure
302-- in response to a command-line option.
303options :: [OptDescr (Opt -> IO Opt)]
305 [ Option "fr" ["from","read"]
307 (\arg opt -> return opt { optReader = arg })
311 , Option "tw" ["to","write"]
313 (\arg opt -> return opt { optWriter = arg })
317 , Option "o" ["output"]
319 (\arg opt -> return opt { optOutputFile = arg })
321 "" -- "Name of output file"
323 , Option "" ["data-dir"]
325 (\arg opt -> return opt { optDataDir = Just arg })
326 "DIRECTORY") -- "Directory containing pandoc data files."
329 , Option "R" ["parse-raw"]
331 (\opt -> return opt { optParseRaw = True }))
332 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
334 , Option "S" ["smart"]
336 (\opt -> return opt { optSmart = True }))
337 "" -- "Use smart quotes, dashes, and ellipses"
339 , Option "" ["old-dashes"]
341 (\opt -> return opt { optSmart = True
342 , optOldDashes = True }))
343 "" -- "Use smart quotes, dashes, and ellipses"
345 , Option "" ["base-header-level"]
350 let oldTransforms = optTransforms opt
352 return opt{ optTransforms =
353 headerShift shift : oldTransforms }
355 "base-header-level must be a number > 0")
357 "" -- "Headers base level"
359 , Option "" ["indented-code-classes"]
361 (\arg opt -> return opt { optIndentedCodeClasses = words $
362 map (\c -> if c == ',' then ' ' else c) arg })
364 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
366 , Option "F" ["filter"]
368 (\arg opt -> return opt { optFilters = arg : optFilters opt })
370 "" -- "External JSON filter"
372 , Option "" ["normalize"]
374 (\opt -> return opt { optTransforms =
375 normalize : optTransforms opt } ))
376 "" -- "Normalize the Pandoc AST"
378 , Option "p" ["preserve-tabs"]
380 (\opt -> return opt { optPreserveTabs = True }))
381 "" -- "Preserve tabs instead of converting to spaces"
383 , Option "" ["tab-stop"]
387 Just t | t > 0 -> return opt { optTabStop = t }
389 "tab-stop must be a number greater than 0")
391 "" -- "Tab stop (default 4)"
393 , Option "" ["track-changes"]
396 action <- case arg of
397 "accept" -> return AcceptChanges
398 "reject" -> return RejectChanges
399 "all" -> return AllChanges
401 ("Unknown option for track-changes: " ++ arg)
402 return opt { optTrackChanges = action })
404 "" -- "Accepting or reject MS Word track-changes.""
406 , Option "" ["file-scope"]
408 (\opt -> return opt { optFileScope = True }))
409 "" -- "Parse input files before combining"
411 , Option "" ["extract-media"]
414 return opt { optExtractMedia = Just arg })
416 "" -- "Directory to which to extract embedded media"
418 , Option "s" ["standalone"]
420 (\opt -> return opt { optStandalone = True }))
421 "" -- "Include needed header and footer on output"
423 , Option "" ["template"]
426 return opt{ optTemplate = Just arg,
427 optStandalone = True })
429 "" -- "Use custom template"
431 , Option "M" ["metadata"]
434 let (key,val) = case break (`elem` ":=") arg of
435 (k,_:v) -> (k, readMetaValue v)
436 (k,_) -> (k, MetaBool True)
437 return opt{ optMetadata = addMetadata key val
442 , Option "V" ["variable"]
445 let (key,val) = case break (`elem` ":=") arg of
448 return opt{ optVariables = (key,val) : optVariables opt })
452 , Option "D" ["print-default-template"]
455 templ <- getDefaultTemplate Nothing arg
457 Right t -> UTF8.hPutStr stdout t
458 Left e -> error $ show e
461 "" -- "Print default template for FORMAT"
463 , Option "" ["print-default-data-file"]
466 readDataFile Nothing arg >>= BS.hPutStr stdout
469 "" -- "Print default data file"
475 Just t | t > 0 -> return opt { optDpi = t }
477 "dpi must be a number greater than 0")
479 "" -- "Dpi (default 96)"
481 , Option "" ["no-wrap"]
483 (\opt -> do warn $ "--no-wrap is deprecated. " ++
484 "Use --wrap=none or --wrap=preserve instead."
485 return opt { optWrapText = WrapNone }))
491 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
492 Just o -> return opt { optWrapText = o }
493 Nothing -> err 77 "--wrap must be auto, none, or preserve")
494 "[auto|none|preserve]")
495 "" -- "Option for wrapping text in output"
497 , Option "" ["columns"]
501 Just t | t > 0 -> return opt { optColumns = t }
503 "columns must be a number greater than 0")
505 "" -- "Length of line in characters"
507 , Option "" ["toc", "table-of-contents"]
509 (\opt -> return opt { optTableOfContents = True }))
510 "" -- "Include table of contents"
512 , Option "" ["toc-depth"]
516 Just t | t >= 1 && t <= 6 ->
517 return opt { optTOCDepth = t }
519 "TOC level must be a number between 1 and 6")
521 "" -- "Number of levels to include in TOC"
523 , Option "" ["no-highlight"]
525 (\opt -> return opt { optHighlight = False }))
526 "" -- "Don't highlight source code"
528 , Option "" ["highlight-style"]
531 case lookup (map toLower arg) highlightingStyles of
532 Just s -> return opt{ optHighlightStyle = s }
533 Nothing -> err 39 $ "Unknown style: " ++ arg)
535 "" -- "Style for highlighted code"
537 , Option "H" ["include-in-header"]
540 text <- UTF8.readFile arg
541 -- add new ones to end, so they're included in order specified
542 let newvars = optVariables opt ++ [("header-includes",text)]
543 return opt { optVariables = newvars,
544 optStandalone = True })
546 "" -- "File to include at end of header (implies -s)"
548 , Option "B" ["include-before-body"]
551 text <- UTF8.readFile arg
552 -- add new ones to end, so they're included in order specified
553 let newvars = optVariables opt ++ [("include-before",text)]
554 return opt { optVariables = newvars,
555 optStandalone = True })
557 "" -- "File to include before document body"
559 , Option "A" ["include-after-body"]
562 text <- UTF8.readFile arg
563 -- add new ones to end, so they're included in order specified
564 let newvars = optVariables opt ++ [("include-after",text)]
565 return opt { optVariables = newvars,
566 optStandalone = True })
568 "" -- "File to include after document body"
570 , Option "" ["self-contained"]
572 (\opt -> return opt { optSelfContained = True,
573 optStandalone = True }))
574 "" -- "Make slide shows include all the needed js and css"
576 , Option "" ["html-q-tags"]
579 return opt { optHtmlQTags = True }))
580 "" -- "Use <q> tags for quotes in HTML"
582 , Option "" ["ascii"]
584 (\opt -> return opt { optAscii = True }))
585 "" -- "Use ascii characters only in HTML output"
587 , Option "" ["reference-links"]
589 (\opt -> return opt { optReferenceLinks = True } ))
590 "" -- "Use reference links in parsing HTML"
592 , Option "" ["reference-location"]
595 action <- case arg of
596 "block" -> return EndOfBlock
597 "section" -> return EndOfSection
598 "document" -> return EndOfDocument
600 ("Unknown option for reference-location: " ++ arg)
601 return opt { optReferenceLocation = action })
602 "block|section|document")
603 "" -- "Accepting or reject MS Word track-changes.""
605 , Option "" ["atx-headers"]
607 (\opt -> return opt { optSetextHeaders = False } ))
608 "" -- "Use atx-style headers for markdown"
610 , Option "" ["chapters"]
612 (\opt -> do warn $ "--chapters is deprecated. " ++
613 "Use --top-level-division=chapter instead."
614 return opt { optTopLevelDivision = Chapter }))
615 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
617 , Option "" ["top-level-division"]
619 (\arg opt -> case safeRead (uppercaseFirstLetter arg) of
620 Just dvsn -> return opt { optTopLevelDivision = dvsn }
621 _ -> err 76 "Top-level division must be section, chapter, or part")
622 "[section|chapter|part]")
623 "" -- "Use top-level division type in LaTeX, ConTeXt, DocBook"
625 , Option "N" ["number-sections"]
627 (\opt -> return opt { optNumberSections = True }))
628 "" -- "Number sections in LaTeX"
630 , Option "" ["number-offset"]
633 case safeRead ('[':arg ++ "]") of
634 Just ns -> return opt { optNumberOffset = ns,
635 optNumberSections = True }
636 _ -> err 57 "could not parse number-offset")
638 "" -- "Starting number for sections, subsections, etc."
640 , Option "" ["no-tex-ligatures"]
642 (\opt -> return opt { optTeXLigatures = False }))
643 "" -- "Don't use tex ligatures for quotes, dashes"
645 , Option "" ["listings"]
647 (\opt -> return opt { optListings = True }))
648 "" -- "Use listings package for LaTeX code blocks"
650 , Option "i" ["incremental"]
652 (\opt -> return opt { optIncremental = True }))
653 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
655 , Option "" ["slide-level"]
659 Just t | t >= 1 && t <= 6 ->
660 return opt { optSlideLevel = Just t }
662 "slide level must be a number between 1 and 6")
664 "" -- "Force header level for slides"
666 , Option "" ["section-divs"]
668 (\opt -> return opt { optSectionDivs = True }))
669 "" -- "Put sections in div tags in HTML"
671 , Option "" ["default-image-extension"]
673 (\arg opt -> return opt { optDefaultImageExtension = arg })
675 "" -- "Default extension for extensionless images"
677 , Option "" ["email-obfuscation"]
680 method <- case arg of
681 "references" -> return ReferenceObfuscation
682 "javascript" -> return JavascriptObfuscation
683 "none" -> return NoObfuscation
685 ("Unknown obfuscation method: " ++ arg)
686 return opt { optEmailObfuscation = method })
687 "none|javascript|references")
688 "" -- "Method for obfuscating email in HTML"
690 , Option "" ["id-prefix"]
692 (\arg opt -> return opt { optIdentifierPrefix = arg })
694 "" -- "Prefix to add to automatically generated HTML identifiers"
696 , Option "T" ["title-prefix"]
699 let newvars = ("title-prefix", arg) : optVariables opt
700 return opt { optVariables = newvars,
701 optStandalone = True })
703 "" -- "String to prefix to HTML window title"
708 -- add new link to end, so it is included in proper order
709 let newvars = optVariables opt ++ [("css",arg)]
710 return opt { optVariables = newvars,
711 optStandalone = True })
713 "" -- "Link to CSS style sheet"
715 , Option "" ["reference-odt"]
718 return opt { optReferenceODT = Just arg })
720 "" -- "Path of custom reference.odt"
722 , Option "" ["reference-docx"]
725 return opt { optReferenceDocx = Just arg })
727 "" -- "Path of custom reference.docx"
729 , Option "" ["epub-stylesheet"]
732 text <- UTF8.readFile arg
733 return opt { optEpubStylesheet = Just text })
735 "" -- "Path of epub.css"
737 , Option "" ["epub-cover-image"]
740 return opt { optVariables =
741 ("epub-cover-image", arg) : optVariables opt })
743 "" -- "Path of epub cover image"
745 , Option "" ["epub-metadata"]
748 text <- UTF8.readFile arg
749 return opt { optEpubMetadata = text })
751 "" -- "Path of epub metadata file"
753 , Option "" ["epub-embed-font"]
756 return opt{ optEpubFonts = arg : optEpubFonts opt })
758 "" -- "Directory of fonts to embed"
760 , Option "" ["epub-chapter-level"]
764 Just t | t >= 1 && t <= 6 ->
765 return opt { optEpubChapterLevel = t }
767 "chapter level must be a number between 1 and 6")
769 "" -- "Header level at which to split chapters in EPUB"
771 , Option "" ["latex-engine"]
774 let b = takeBaseName arg
775 if b `elem` ["pdflatex", "lualatex", "xelatex"]
776 then return opt { optLaTeXEngine = arg }
777 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
779 "" -- "Name of latex program to use in generating PDF"
781 , Option "" ["latex-engine-opt"]
784 let oldArgs = optLaTeXEngineArgs opt
785 return opt { optLaTeXEngineArgs = arg : oldArgs })
787 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
789 , Option "" ["bibliography"]
791 (\arg opt -> return opt{ optMetadata = addMetadata
792 "bibliography" (readMetaValue arg)
801 return opt{ optMetadata = addMetadata "csl"
807 , Option "" ["citation-abbreviations"]
810 return opt{ optMetadata = addMetadata
811 "citation-abbreviations"
817 , Option "" ["natbib"]
819 (\opt -> return opt { optCiteMethod = Natbib }))
820 "" -- "Use natbib cite commands in LaTeX output"
822 , Option "" ["biblatex"]
824 (\opt -> return opt { optCiteMethod = Biblatex }))
825 "" -- "Use biblatex cite commands in LaTeX output"
827 , Option "m" ["latexmathml", "asciimathml"]
830 return opt { optHTMLMathMethod = LaTeXMathML arg })
832 "" -- "Use LaTeXMathML script in html output"
834 , Option "" ["mathml"]
837 return opt { optHTMLMathMethod = MathML arg })
839 "" -- "Use mathml for HTML math"
841 , Option "" ["mimetex"]
844 let url' = case arg of
846 Nothing -> "/cgi-bin/mimetex.cgi?"
847 return opt { optHTMLMathMethod = WebTeX url' })
849 "" -- "Use mimetex for HTML math"
851 , Option "" ["webtex"]
854 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
855 return opt { optHTMLMathMethod = WebTeX url' })
857 "" -- "Use web service for HTML math"
859 , Option "" ["jsmath"]
861 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
863 "" -- "Use jsMath for HTML math"
865 , Option "" ["mathjax"]
868 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
869 return opt { optHTMLMathMethod = MathJax url'})
871 "" -- "Use MathJax for HTML math"
872 , Option "" ["katex"]
877 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
879 "" -- Use KaTeX for HTML Math
881 , Option "" ["katex-stylesheet"]
884 return opt { optKaTeXStylesheet = Just arg })
886 "" -- Set the KaTeX Stylesheet location
888 , Option "" ["gladtex"]
890 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
891 "" -- "Use gladtex for HTML math"
893 , Option "" ["trace"]
895 (\opt -> return opt { optTrace = True }))
896 "" -- "Turn on diagnostic tracing in readers."
898 , Option "" ["dump-args"]
900 (\opt -> return opt { optDumpArgs = True }))
901 "" -- "Print output filename and arguments to stdout."
903 , Option "" ["ignore-args"]
905 (\opt -> return opt { optIgnoreArgs = True }))
906 "" -- "Ignore command-line arguments."
908 , Option "" ["verbose"]
910 (\opt -> return opt { optVerbose = True }))
911 "" -- "Verbose diagnostic output."
913 , Option "" ["bash-completion"]
917 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
918 let optnames (Option shorts longs _ _) =
919 map (\c -> ['-',c]) shorts ++
921 let allopts = unwords (concatMap optnames options)
922 UTF8.hPutStrLn stdout $ printf tpl allopts
923 (unwords (map fst readers))
924 (unwords (map fst writers))
927 "" -- "Print bash completion script"
929 , Option "" ["list-input-formats"]
932 let readers'names = sort (map fst readers)
933 mapM_ (UTF8.hPutStrLn stdout) readers'names
937 , Option "" ["list-output-formats"]
940 let writers'names = sort (map fst writers)
941 mapM_ (UTF8.hPutStrLn stdout) writers'names
945 , Option "" ["list-extensions"]
948 let showExt x = drop 4 (show x) ++
949 if x `Set.member` pandocExtensions
952 mapM_ (UTF8.hPutStrLn stdout . showExt)
953 ([minBound..maxBound] :: [Extension])
957 , Option "" ["list-highlight-languages"]
960 let langs = [map toLower l | l <- languages,
961 l /= "Alert" && l /= "Alert_indent"]
962 mapM_ (UTF8.hPutStrLn stdout) langs
966 , Option "" ["list-highlight-styles"]
969 mapM_ (UTF8.hPutStrLn stdout) $
970 map fst highlightingStyles
974 , Option "v" ["version"]
978 defaultDatadir <- E.catch
979 (getAppUserDataDirectory "pandoc")
980 (\e -> let _ = (e :: E.SomeException)
982 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
983 compileInfo ++ "\nDefault user data directory: " ++
984 defaultDatadir ++ copyrightMessage)
986 "" -- "Print version"
988 , Option "h" ["help"]
992 UTF8.hPutStr stdout (usageMessage prg options)
999addMetadata :: String -> MetaValue -> M.Map String MetaValue
1000 -> M.Map String MetaValue
1001addMetadata k v m = case M.lookup k m of
1002 Nothing -> M.insert k v m
1003 Just (MetaList xs) -> M.insert k
1004 (MetaList (xs ++ [v])) m
1005 Just x -> M.insert k (MetaList [v, x]) m
1007readMetaValue :: String -> MetaValue
1008readMetaValue s = case decode (UTF8.fromString s) of
1009 Just (Yaml.String t) -> MetaString $ T.unpack t
1010 Just (Yaml.Bool b) -> MetaBool b
1013-- Returns usage message
1014usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
1015usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]")
1017-- Determine default reader based on source file extensions
1018defaultReaderName :: String -> [FilePath] -> String
1019defaultReaderName fallback [] = fallback
1020defaultReaderName fallback (x:xs) =
1021 case takeExtension (map toLower x) of
1026 ".markdown" -> "markdown"
1032 ".lhs" -> "markdown+lhs"
1035 ".wiki" -> "mediawiki"
1036 ".dokuwiki" -> "dokuwiki"
1037 ".textile" -> "textile"
1038 ".native" -> "native"
1044 ".pdf" -> "pdf" -- so we get an "unknown reader" error
1045 ".doc" -> "doc" -- so we get an "unknown reader" error
1046 _ -> defaultReaderName fallback xs
1048-- Returns True if extension of first source is .lhs
1049lhsExtension :: [FilePath] -> Bool
1050lhsExtension (x:_) = takeExtension x == ".lhs"
1051lhsExtension _ = False
1053-- Determine default writer based on output file extension
1054defaultWriterName :: FilePath -> String
1055defaultWriterName "-" = "html" -- no output file
1056defaultWriterName x =
1057 case takeExtension (map toLower x) of
1058 "" -> "markdown" -- empty extension
1062 ".context" -> "context"
1067 ".native" -> "native"
1069 ".txt" -> "markdown"
1070 ".text" -> "markdown"
1072 ".markdown" -> "markdown"
1073 ".textile" -> "textile"
1074 ".lhs" -> "markdown+lhs"
1075 ".texi" -> "texinfo"
1076 ".texinfo" -> "texinfo"
1082 ".asciidoc" -> "asciidoc"
1083 ".adoc" -> "asciidoc"
1090 ['.',y] | y `elem` ['1'..'9'] -> "man"
1093-- Transformations of a Pandoc document post-parsing:
1095extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1096extractMedia media dir d =
1097 case [fp | (fp, _, _) <- mediaDirectory media] of
1100 extractMediaBag True dir media
1101 return $ walk (adjustImagePath dir fps) d
1103adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1104adjustImagePath dir paths (Image attr lab (src, tit))
1105 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1106adjustImagePath _ _ x = x
1108adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1109adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1111applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1112applyTransforms transforms d = return $ foldr ($) d transforms
1114 -- First we check to see if a filter is a path. If it isn't, we
1115 -- check to see whether it's in `userdir/filters`. If not, we leave
1117expandFilterPath :: Maybe FilePath -> FilePath -> IO FilePath
1118expandFilterPath mbDatadir fp
1119 | '/' `elem` fp = return fp
1120 | Just datadir <- mbDatadir = do
1121 let filterPath = (datadir </> "filters" </> fp)
1122 filterPathExists <- doesFileExist filterPath
1124 then return filterPath
1126 | otherwise = return fp
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 ""
1304 deftemp <- getDefaultTemplate datadir format
1309 -- strip off extensions
1310 let tp' = case takeExtension tp of
1313 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 MathML Nothing -> do
1327 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1328 return $ ("mathml-script", s) : variables
1329 _ -> return variables
1331 variables'' <- if format == "dzslides"
1333 dztempl <- readDataFileUTF8 datadir
1334 ("dzslides" </> "template.html")
1335 let dzline = "<!-- {{{{ dzslides core"
1336 let dzcore = unlines
1337 $ dropWhile (not . (dzline `isPrefixOf`))
1339 return $ ("dzslides-core", dzcore) : variables'
1340 else return variables'
1342 let sourceURL = case sources of
1344 (x:_) -> case parseURI x of
1346 | uriScheme u `elem` ["http:","https:"] ->
1347 Just $ show u{ uriQuery = "",
1351 let readerOpts = def{ readerSmart = if laTeXInput
1353 else smart || (texLigatures &&
1354 (laTeXOutput || conTeXtOutput))
1355 , readerStandalone = standalone'
1356 , readerParseRaw = parseRaw
1357 , readerColumns = columns
1358 , readerTabStop = tabStop
1359 , readerOldDashes = oldDashes
1360 , readerIndentedCodeClasses = codeBlockClasses
1361 , readerApplyMacros = not laTeXOutput
1362 , readerDefaultImageExtension = defaultImageExtension
1363 , readerTrace = trace
1364 , readerTrackChanges = trackChanges
1365 , readerFileScope = fileScope
1371 istty <- queryTerminal stdOutput
1373 when (istty && not (isTextFormat format) && outputFile == "-") $
1374 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1375 "Specify an output file using the -o option."
1377 let readSources [] = mapM readSource ["-"]
1378 readSources srcs = mapM readSource srcs
1379 readSource "-" = UTF8.getContents
1380 readSource src = case parseURI src of
1381 Just u | uriScheme u `elem` ["http:","https:"] ->
1383 | uriScheme u == "file:" ->
1384 UTF8.readFile (uriPath u)
1385 _ -> UTF8.readFile src
1390 Right (bs,_) -> return $ UTF8.toString bs
1392 let readFiles [] = error "Cannot read archive from stdin"
1393 readFiles [x] = B.readFile x
1394 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1396 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1400 let handleIncludes' :: String -> IO (Either PandocError String)
1401 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1405 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1406 sourceToDoc sources' = fmap handleError $
1409 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1410 doc <- handleIncludes' srcs
1411 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1412 ByteStringReader r -> readFiles sources' >>= r readerOpts
1414 -- We parse first if (1) fileScope is set, (2), it's a binary
1415 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1416 -- of negatives as opposed to an OR of positives, so we do default
1417 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1419 (doc, media) <- case reader of
1420 (StringReader _) | not fileScope && readerName' /= "json" ->
1422 _ | null sources -> sourceToDoc sources
1423 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1424 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1426 let writerOptions = def { writerStandalone = standalone',
1427 writerTemplate = templ,
1428 writerVariables = variables'',
1429 writerTabStop = tabStop,
1430 writerTableOfContents = toc,
1431 writerHTMLMathMethod = mathMethod,
1432 writerIncremental = incremental,
1433 writerCiteMethod = citeMethod,
1434 writerIgnoreNotes = False,
1435 writerNumberSections = numberSections,
1436 writerNumberOffset = numberFrom,
1437 writerSectionDivs = sectionDivs,
1438 writerReferenceLinks = referenceLinks,
1439 writerReferenceLocation = referenceLocation,
1441 writerWrapText = wrap,
1442 writerColumns = columns,
1443 writerEmailObfuscation = obfuscationMethod,
1444 writerIdentifierPrefix = idPrefix,
1445 writerSourceURL = sourceURL,
1446 writerUserDataDir = datadir,
1447 writerHtml5 = html5,
1448 writerHtmlQTags = htmlQTags,
1449 writerTopLevelDivision = topLevelDivision,
1450 writerListings = listings,
1451 writerBeamer = False,
1452 writerSlideLevel = slideLevel,
1453 writerHighlight = highlight,
1454 writerHighlightStyle = highlightStyle,
1455 writerSetextHeaders = setextHeaders,
1456 writerTeXLigatures = texLigatures,
1457 writerEpubMetadata = epubMetadata,
1458 writerEpubStylesheet = epubStylesheet,
1459 writerEpubFonts = epubFonts,
1460 writerEpubChapterLevel = epubChapterLevel,
1461 writerTOCDepth = epubTOCDepth,
1462 writerReferenceODT = referenceODT,
1463 writerReferenceDocx = referenceDocx,
1464 writerMediaBag = media,
1465 writerVerbose = verbose,
1466 writerLaTeXArgs = latexEngineArgs
1470 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1471 adjustMetadata metadata >=>
1472 applyTransforms transforms >=>
1473 applyFilters datadir filters' [format]) doc
1475 let writeFnBinary :: FilePath -> B.ByteString -> IO ()
1476 writeFnBinary "-" = B.putStr
1477 writeFnBinary f = B.writeFile (UTF8.encodePath f)
1479 let writerFn :: FilePath -> String -> IO ()
1480 writerFn "-" = UTF8.putStr
1481 writerFn f = UTF8.writeFile f
1484 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1485 IOByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
1488 -- make sure writer is latex or beamer or context or html5
1489 unless (laTeXOutput || conTeXtOutput || html5Output) $
1490 err 47 $ "cannot produce pdf output with " ++ format ++
1493 let pdfprog = case () of
1494 _ | conTeXtOutput -> "context"
1495 _ | html5Output -> "wkhtmltopdf"
1497 -- check for pdf creating program
1498 mbPdfProg <- findExecutable pdfprog
1499 when (isNothing mbPdfProg) $
1500 err 41 $ pdfprog ++ " not found. " ++
1501 pdfprog ++ " is needed for pdf output."
1503 res <- makePDF pdfprog f writerOptions doc'
1505 Right pdf -> writeFnBinary outputFile pdf
1507 B.hPutStr stderr err'
1508 B.hPut stderr $ B.pack [10]
1509 err 43 "Error producing PDF"
1510 | otherwise -> selfcontain (f writerOptions doc' ++
1511 ['\n' | not standalone'])
1512 >>= writerFn outputFile . handleEntities
1513 where htmlFormat = format `elem`
1514 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1515 selfcontain = if selfContained && htmlFormat
1516 then makeSelfContained writerOptions
1518 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