1{-# LANGUAGE CPP, TupleSections, ScopedTypeVariables #-}
3Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 Copyright : Copyright (C) 2006-2016 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
34import Text.Pandoc.Builder (setMeta)
35import Text.Pandoc.PDF (makePDF)
36import Text.Pandoc.Walk (walk)
37import Text.Pandoc.Readers.LaTeX (handleIncludes)
38import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
39 safeRead, headerShift, normalize, err, warn,
41import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag )
42import Text.Pandoc.XML ( toEntities )
43import Text.Pandoc.SelfContained ( makeSelfContained )
44import Text.Pandoc.Process (pipeProcess)
45import Text.Highlighting.Kate ( languages, Style, tango, pygments,
46 espresso, zenburn, kate, haddock, monochrome )
47import System.Environment ( getArgs, getProgName )
48import System.Exit ( ExitCode (..), exitSuccess )
50import System.Console.GetOpt
51import Data.Char ( toLower, toUpper )
52import Data.List ( delete, intercalate, isPrefixOf, isSuffixOf, sort )
53import System.Directory ( getAppUserDataDirectory, findExecutable,
54 doesFileExist, Permissions(..), getPermissions )
55import System.IO ( stdout, stderr )
56import System.IO.Error ( isDoesNotExistError )
57import qualified Control.Exception as E
58import Control.Exception.Extensible ( throwIO )
59import qualified Text.Pandoc.UTF8 as UTF8
60import Control.Monad (when, unless, (>=>))
61import Data.Maybe (fromMaybe, isNothing, isJust)
62import Data.Foldable (foldrM)
63import Network.URI (parseURI, isURI, URI(..))
64import qualified Data.ByteString.Lazy as B
65import qualified Data.ByteString as BS
66import Data.Aeson (eitherDecode', encode)
67import qualified Data.Map as M
68import Data.Yaml (decode)
69import qualified Data.Yaml as Yaml
70import qualified Data.Text as T
71import Control.Applicative ((<|>))
72import Text.Pandoc.Readers.Txt2Tags (getT2TMeta)
73import Paths_pandoc (getDataDir)
74import Text.Printf (printf)
75import Text.Pandoc.Error
77type Transform = Pandoc -> Pandoc
79copyrightMessage :: String
80copyrightMessage = intercalate "\n" [
82 "Copyright (C) 2006-2016 John MacFarlane",
83 "Web: http://pandoc.org",
84 "This is free software; see the source for copying conditions.",
85 "There is no warranty, not even for merchantability or fitness",
86 "for a particular purpose." ]
90 "\nCompiled with texmath " ++
91 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
92 ".\nSyntax highlighting is supported for the following languages:\n " ++
94 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
96-- | Converts a list of strings into a single string with the items printed as
97-- comma separated words in lines with a maximum line length.
98wrapWords :: Int -> Int -> [String] -> String
99wrapWords indent c = wrap' (c - indent) (c - indent)
102 wrap' cols remaining (x:xs)
103 | remaining == cols =
104 x ++ wrap' cols (remaining - length x) xs
105 | (length x + 1) > remaining =
106 ",\n" ++ replicate indent ' ' ++ x ++
107 wrap' cols (cols - length x) xs
110 wrap' cols (remaining - length x - 2) xs
112isTextFormat :: String -> Bool
113isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
115externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
116externalFilter f args' d = do
117 mbexe <- if '/' `elem` f
118 -- don't check PATH if filter name has a path
120 -- we catch isDoesNotExistError because this will
121 -- be triggered if PATH not set:
122 else E.catch (findExecutable f)
123 (\e -> if isDoesNotExistError e
126 (f', args'') <- case mbexe of
127 Just x -> return (x, args')
129 exists <- doesFileExist f
132 isExecutable <- executable `fmap`
135 case map toLower $ takeExtension f of
136 _ | isExecutable -> (f, args')
137 ".py" -> ("python", f:args')
138 ".hs" -> ("runhaskell", f:args')
139 ".pl" -> ("perl", f:args')
140 ".rb" -> ("ruby", f:args')
141 ".php" -> ("php", f:args')
143 else err 85 $ "Filter " ++ f ++ " not found"
145 mbExe <- findExecutable f'
146 when (isNothing mbExe) $
147 err 83 $ "Error running filter " ++ f ++ "\n" ++
148 show f' ++ " not found in path."
149 (exitcode, outbs, errbs) <- E.handle filterException $
150 pipeProcess Nothing f' args'' $ encode d
151 unless (B.null errbs) $ B.hPutStr stderr errbs
153 ExitSuccess -> return $ either error id $ eitherDecode' outbs
154 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
155 "Filter returned error status " ++ show ec
156 where filterException :: E.SomeException -> IO a
157 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
160-- | Data structure for command line options.
162 { optTabStop :: Int -- ^ Number of spaces per tab
163 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
164 , optStandalone :: Bool -- ^ Include header, footer
165 , optReader :: String -- ^ Reader format
166 , optWriter :: String -- ^ Writer format
167 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
168 , optTableOfContents :: Bool -- ^ Include table of contents
169 , optTransforms :: [Transform] -- ^ Doc transforms to apply
170 , optTemplate :: Maybe FilePath -- ^ Custom template
171 , optVariables :: [(String,String)] -- ^ Template variables to set
172 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
173 , optOutputFile :: String -- ^ Name of output file
174 , optNumberSections :: Bool -- ^ Number sections in LaTeX
175 , optNumberOffset :: [Int] -- ^ Starting number for sections
176 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
177 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
178 , optSelfContained :: Bool -- ^ Make HTML accessible offline
179 , optSmart :: Bool -- ^ Use smart typography
180 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
181 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
182 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
183 , optHighlight :: Bool -- ^ Highlight source code
184 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
185 , optChapters :: Bool -- ^ Use chapter for top-level sects
186 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
187 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
188 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
189 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
190 , optEpubMetadata :: String -- ^ EPUB metadata
191 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
192 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
193 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
194 , optDumpArgs :: Bool -- ^ Output command-line arguments
195 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
196 , optVerbose :: Bool -- ^ Verbose diagnostic output
197 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
198 , optDpi :: Int -- ^ Dpi
199 , optWrapText :: WrapOption -- ^ Options for wrapping text
200 , optColumns :: Int -- ^ Line length in characters
201 , optFilters :: [FilePath] -- ^ Filters to apply
202 , optEmailObfuscation :: ObfuscationMethod
203 , optIdentifierPrefix :: String
204 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
205 , optDataDir :: Maybe FilePath
206 , optCiteMethod :: CiteMethod -- ^ Method to output cites
207 , optListings :: Bool -- ^ Use listings package for code blocks
208 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
209 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
210 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
211 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
212 , optAscii :: Bool -- ^ Use ascii characters only in html
213 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
214 , optDefaultImageExtension :: String -- ^ Default image extension
215 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
216 , optTrace :: Bool -- ^ Print debug information
217 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
218 , optFileScope :: Bool -- ^ Parse input files before combining
219 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
220 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
223-- | Defaults for command-line options.
227 , optPreserveTabs = False
228 , optStandalone = False
229 , optReader = "" -- null for default reader
230 , optWriter = "" -- null for default writer
231 , optParseRaw = False
232 , optTableOfContents = False
234 , optTemplate = Nothing
236 , optMetadata = M.empty
237 , optOutputFile = "-" -- "-" means stdout
238 , optNumberSections = False
239 , optNumberOffset = [0,0,0,0,0,0]
240 , optSectionDivs = False
241 , optIncremental = False
242 , optSelfContained = False
244 , optOldDashes = False
246 , optHtmlQTags = False
247 , optHighlight = True
248 , optHighlightStyle = pygments
249 , optChapters = False
250 , optHTMLMathMethod = PlainMath
251 , optReferenceODT = Nothing
252 , optReferenceDocx = Nothing
253 , optEpubStylesheet = Nothing
254 , optEpubMetadata = ""
256 , optEpubChapterLevel = 1
258 , optDumpArgs = False
259 , optIgnoreArgs = False
261 , optReferenceLinks = False
263 , optWrapText = WrapAuto
266 , optEmailObfuscation = JavascriptObfuscation
267 , optIdentifierPrefix = ""
268 , optIndentedCodeClasses = []
269 , optDataDir = Nothing
270 , optCiteMethod = Citeproc
271 , optListings = False
272 , optLaTeXEngine = "pdflatex"
273 , optLaTeXEngineArgs = []
274 , optSlideLevel = Nothing
275 , optSetextHeaders = True
277 , optTeXLigatures = True
278 , optDefaultImageExtension = ""
279 , optExtractMedia = Nothing
281 , optTrackChanges = AcceptChanges
282 , optFileScope = False
283 , optKaTeXStylesheet = Nothing
284 , optKaTeXJS = Nothing
287-- | A list of functions, each transforming the options data structure
288-- in response to a command-line option.
289options :: [OptDescr (Opt -> IO Opt)]
291 [ Option "fr" ["from","read"]
293 (\arg opt -> return opt { optReader = arg })
297 , Option "tw" ["to","write"]
299 (\arg opt -> return opt { optWriter = arg })
303 , Option "o" ["output"]
305 (\arg opt -> return opt { optOutputFile = arg })
307 "" -- "Name of output file"
309 , Option "" ["data-dir"]
311 (\arg opt -> return opt { optDataDir = Just arg })
312 "DIRECTORY") -- "Directory containing pandoc data files."
315 , Option "R" ["parse-raw"]
317 (\opt -> return opt { optParseRaw = True }))
318 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
320 , Option "S" ["smart"]
322 (\opt -> return opt { optSmart = True }))
323 "" -- "Use smart quotes, dashes, and ellipses"
325 , Option "" ["old-dashes"]
327 (\opt -> return opt { optSmart = True
328 , optOldDashes = True }))
329 "" -- "Use smart quotes, dashes, and ellipses"
331 , Option "" ["base-header-level"]
336 let oldTransforms = optTransforms opt
338 return opt{ optTransforms =
339 headerShift shift : oldTransforms }
341 "base-header-level must be a number > 0")
343 "" -- "Headers base level"
345 , Option "" ["indented-code-classes"]
347 (\arg opt -> return opt { optIndentedCodeClasses = words $
348 map (\c -> if c == ',' then ' ' else c) arg })
350 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
352 , Option "F" ["filter"]
354 (\arg opt -> return opt { optFilters = arg : optFilters opt })
356 "" -- "External JSON filter"
358 , Option "" ["normalize"]
360 (\opt -> return opt { optTransforms =
361 normalize : optTransforms opt } ))
362 "" -- "Normalize the Pandoc AST"
364 , Option "p" ["preserve-tabs"]
366 (\opt -> return opt { optPreserveTabs = True }))
367 "" -- "Preserve tabs instead of converting to spaces"
369 , Option "" ["tab-stop"]
373 Just t | t > 0 -> return opt { optTabStop = t }
375 "tab-stop must be a number greater than 0")
377 "" -- "Tab stop (default 4)"
379 , Option "" ["track-changes"]
382 action <- case arg of
383 "accept" -> return AcceptChanges
384 "reject" -> return RejectChanges
385 "all" -> return AllChanges
387 ("Unknown option for track-changes: " ++ arg)
388 return opt { optTrackChanges = action })
390 "" -- "Accepting or reject MS Word track-changes.""
392 , Option "" ["file-scope"]
394 (\opt -> return opt { optFileScope = True }))
395 "" -- "Parse input files before combining"
397 , Option "" ["extract-media"]
400 return opt { optExtractMedia = Just arg })
402 "" -- "Directory to which to extract embedded media"
404 , Option "s" ["standalone"]
406 (\opt -> return opt { optStandalone = True }))
407 "" -- "Include needed header and footer on output"
409 , Option "" ["template"]
412 return opt{ optTemplate = Just arg,
413 optStandalone = True })
415 "" -- "Use custom template"
417 , Option "M" ["metadata"]
420 let (key,val) = case break (`elem` ":=") arg of
421 (k,_:v) -> (k, readMetaValue v)
422 (k,_) -> (k, MetaBool True)
423 return opt{ optMetadata = addMetadata key val
428 , Option "V" ["variable"]
431 let (key,val) = case break (`elem` ":=") arg of
434 return opt{ optVariables = (key,val) : optVariables opt })
438 , Option "D" ["print-default-template"]
441 templ <- getDefaultTemplate Nothing arg
443 Right t -> UTF8.hPutStr stdout t
444 Left e -> error $ show e
447 "" -- "Print default template for FORMAT"
449 , Option "" ["print-default-data-file"]
452 readDataFile Nothing arg >>= BS.hPutStr stdout
455 "" -- "Print default data file"
461 Just t | t > 0 -> return opt { optDpi = t }
463 "dpi must be a number greater than 0")
465 "" -- "Dpi (default 96)"
467 , Option "" ["no-wrap"]
469 (\opt -> do warn $ "--no-wrap is deprecated. " ++
470 "Use --wrap=none or --wrap=preserve instead."
471 return opt { optWrapText = WrapNone }))
477 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
478 Just o -> return opt { optWrapText = o }
479 Nothing -> err 77 "--wrap must be auto, none, or preserve")
480 "[auto|none|preserve]")
481 "" -- "Option for wrapping text in output"
483 , Option "" ["columns"]
487 Just t | t > 0 -> return opt { optColumns = t }
489 "columns must be a number greater than 0")
491 "" -- "Length of line in characters"
493 , Option "" ["toc", "table-of-contents"]
495 (\opt -> return opt { optTableOfContents = True }))
496 "" -- "Include table of contents"
498 , Option "" ["toc-depth"]
502 Just t | t >= 1 && t <= 6 ->
503 return opt { optTOCDepth = t }
505 "TOC level must be a number between 1 and 6")
507 "" -- "Number of levels to include in TOC"
509 , Option "" ["no-highlight"]
511 (\opt -> return opt { optHighlight = False }))
512 "" -- "Don't highlight source code"
514 , Option "" ["highlight-style"]
517 newStyle <- case map toLower arg of
518 "pygments" -> return pygments
519 "tango" -> return tango
520 "espresso" -> return espresso
521 "zenburn" -> return zenburn
522 "kate" -> return kate
523 "monochrome" -> return monochrome
524 "haddock" -> return haddock
526 "Unknown style :" ++ arg
527 return opt{ optHighlightStyle = newStyle })
529 "" -- "Style for highlighted code"
531 , Option "H" ["include-in-header"]
534 text <- UTF8.readFile arg
535 -- add new ones to end, so they're included in order specified
536 let newvars = optVariables opt ++ [("header-includes",text)]
537 return opt { optVariables = newvars,
538 optStandalone = True })
540 "" -- "File to include at end of header (implies -s)"
542 , Option "B" ["include-before-body"]
545 text <- UTF8.readFile arg
546 -- add new ones to end, so they're included in order specified
547 let newvars = optVariables opt ++ [("include-before",text)]
548 return opt { optVariables = newvars,
549 optStandalone = True })
551 "" -- "File to include before document body"
553 , Option "A" ["include-after-body"]
556 text <- UTF8.readFile arg
557 -- add new ones to end, so they're included in order specified
558 let newvars = optVariables opt ++ [("include-after",text)]
559 return opt { optVariables = newvars,
560 optStandalone = True })
562 "" -- "File to include after document body"
564 , Option "" ["self-contained"]
566 (\opt -> return opt { optSelfContained = True,
567 optStandalone = True }))
568 "" -- "Make slide shows include all the needed js and css"
570 , Option "" ["html-q-tags"]
573 return opt { optHtmlQTags = True }))
574 "" -- "Use <q> tags for quotes in HTML"
576 , Option "" ["ascii"]
578 (\opt -> return opt { optAscii = True }))
579 "" -- "Use ascii characters only in HTML output"
581 , Option "" ["reference-links"]
583 (\opt -> return opt { optReferenceLinks = True } ))
584 "" -- "Use reference links in parsing HTML"
586 , Option "" ["atx-headers"]
588 (\opt -> return opt { optSetextHeaders = False } ))
589 "" -- "Use atx-style headers for markdown"
591 , Option "" ["chapters"]
593 (\opt -> return opt { optChapters = True }))
594 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
596 , Option "N" ["number-sections"]
598 (\opt -> return opt { optNumberSections = True }))
599 "" -- "Number sections in LaTeX"
601 , Option "" ["number-offset"]
604 case safeRead ('[':arg ++ "]") of
605 Just ns -> return opt { optNumberOffset = ns,
606 optNumberSections = True }
607 _ -> err 57 "could not parse number-offset")
609 "" -- "Starting number for sections, subsections, etc."
611 , Option "" ["no-tex-ligatures"]
613 (\opt -> return opt { optTeXLigatures = False }))
614 "" -- "Don't use tex ligatures for quotes, dashes"
616 , Option "" ["listings"]
618 (\opt -> return opt { optListings = True }))
619 "" -- "Use listings package for LaTeX code blocks"
621 , Option "i" ["incremental"]
623 (\opt -> return opt { optIncremental = True }))
624 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
626 , Option "" ["slide-level"]
630 Just t | t >= 1 && t <= 6 ->
631 return opt { optSlideLevel = Just t }
633 "slide level must be a number between 1 and 6")
635 "" -- "Force header level for slides"
637 , Option "" ["section-divs"]
639 (\opt -> return opt { optSectionDivs = True }))
640 "" -- "Put sections in div tags in HTML"
642 , Option "" ["default-image-extension"]
644 (\arg opt -> return opt { optDefaultImageExtension = arg })
646 "" -- "Default extension for extensionless images"
648 , Option "" ["email-obfuscation"]
651 method <- case arg of
652 "references" -> return ReferenceObfuscation
653 "javascript" -> return JavascriptObfuscation
654 "none" -> return NoObfuscation
656 ("Unknown obfuscation method: " ++ arg)
657 return opt { optEmailObfuscation = method })
658 "none|javascript|references")
659 "" -- "Method for obfuscating email in HTML"
661 , Option "" ["id-prefix"]
663 (\arg opt -> return opt { optIdentifierPrefix = arg })
665 "" -- "Prefix to add to automatically generated HTML identifiers"
667 , Option "T" ["title-prefix"]
670 let newvars = ("title-prefix", arg) : optVariables opt
671 return opt { optVariables = newvars,
672 optStandalone = True })
674 "" -- "String to prefix to HTML window title"
679 -- add new link to end, so it is included in proper order
680 let newvars = optVariables opt ++ [("css",arg)]
681 return opt { optVariables = newvars,
682 optStandalone = True })
684 "" -- "Link to CSS style sheet"
686 , Option "" ["reference-odt"]
689 return opt { optReferenceODT = Just arg })
691 "" -- "Path of custom reference.odt"
693 , Option "" ["reference-docx"]
696 return opt { optReferenceDocx = Just arg })
698 "" -- "Path of custom reference.docx"
700 , Option "" ["epub-stylesheet"]
703 text <- UTF8.readFile arg
704 return opt { optEpubStylesheet = Just text })
706 "" -- "Path of epub.css"
708 , Option "" ["epub-cover-image"]
711 return opt { optVariables =
712 ("epub-cover-image", arg) : optVariables opt })
714 "" -- "Path of epub cover image"
716 , Option "" ["epub-metadata"]
719 text <- UTF8.readFile arg
720 return opt { optEpubMetadata = text })
722 "" -- "Path of epub metadata file"
724 , Option "" ["epub-embed-font"]
727 return opt{ optEpubFonts = arg : optEpubFonts opt })
729 "" -- "Directory of fonts to embed"
731 , Option "" ["epub-chapter-level"]
735 Just t | t >= 1 && t <= 6 ->
736 return opt { optEpubChapterLevel = t }
738 "chapter level must be a number between 1 and 6")
740 "" -- "Header level at which to split chapters in EPUB"
742 , Option "" ["latex-engine"]
745 let b = takeBaseName arg
746 if b `elem` ["pdflatex", "lualatex", "xelatex"]
747 then return opt { optLaTeXEngine = arg }
748 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
750 "" -- "Name of latex program to use in generating PDF"
752 , Option "" ["latex-engine-opt"]
755 let oldArgs = optLaTeXEngineArgs opt
756 return opt { optLaTeXEngineArgs = arg : oldArgs })
758 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
760 , Option "" ["bibliography"]
762 (\arg opt -> return opt{ optMetadata = addMetadata
763 "bibliography" (readMetaValue arg)
772 return opt{ optMetadata = addMetadata "csl"
778 , Option "" ["citation-abbreviations"]
781 return opt{ optMetadata = addMetadata
782 "citation-abbreviations"
788 , Option "" ["natbib"]
790 (\opt -> return opt { optCiteMethod = Natbib }))
791 "" -- "Use natbib cite commands in LaTeX output"
793 , Option "" ["biblatex"]
795 (\opt -> return opt { optCiteMethod = Biblatex }))
796 "" -- "Use biblatex cite commands in LaTeX output"
798 , Option "m" ["latexmathml", "asciimathml"]
801 return opt { optHTMLMathMethod = LaTeXMathML arg })
803 "" -- "Use LaTeXMathML script in html output"
805 , Option "" ["mathml"]
808 return opt { optHTMLMathMethod = MathML arg })
810 "" -- "Use mathml for HTML math"
812 , Option "" ["mimetex"]
815 let url' = case arg of
817 Nothing -> "/cgi-bin/mimetex.cgi?"
818 return opt { optHTMLMathMethod = WebTeX url' })
820 "" -- "Use mimetex for HTML math"
822 , Option "" ["webtex"]
825 let url' = fromMaybe "http://chart.apis.google.com/chart?cht=tx&chl=" arg
826 return opt { optHTMLMathMethod = WebTeX url' })
828 "" -- "Use web service for HTML math"
830 , Option "" ["jsmath"]
832 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
834 "" -- "Use jsMath for HTML math"
836 , Option "" ["mathjax"]
839 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
840 return opt { optHTMLMathMethod = MathJax url'})
842 "" -- "Use MathJax for HTML math"
843 , Option "" ["katex"]
848 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.js"})
850 "" -- Use KaTeX for HTML Math
852 , Option "" ["katex-stylesheet"]
855 return opt { optKaTeXStylesheet = Just arg })
857 "" -- Set the KaTeX Stylesheet location
859 , Option "" ["gladtex"]
861 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
862 "" -- "Use gladtex for HTML math"
864 , Option "" ["trace"]
866 (\opt -> return opt { optTrace = True }))
867 "" -- "Turn on diagnostic tracing in readers."
869 , Option "" ["dump-args"]
871 (\opt -> return opt { optDumpArgs = True }))
872 "" -- "Print output filename and arguments to stdout."
874 , Option "" ["ignore-args"]
876 (\opt -> return opt { optIgnoreArgs = True }))
877 "" -- "Ignore command-line arguments."
879 , Option "" ["verbose"]
881 (\opt -> return opt { optVerbose = True }))
882 "" -- "Verbose diagnostic output."
884 , Option "" ["bash-completion"]
888 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
889 let optnames (Option shorts longs _ _) =
890 map (\c -> ['-',c]) shorts ++
892 let allopts = unwords (concatMap optnames options)
893 UTF8.hPutStrLn stdout $ printf tpl allopts
894 (unwords (map fst readers))
895 (unwords ("pdf": map fst writers))
898 "" -- "Print bash completion script"
900 , Option "v" ["version"]
904 defaultDatadir <- getAppUserDataDirectory "pandoc"
905 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
906 compileInfo ++ "\nDefault user data directory: " ++
907 defaultDatadir ++ copyrightMessage)
909 "" -- "Print version"
911 , Option "h" ["help"]
915 UTF8.hPutStr stdout (usageMessage prg options)
922addMetadata :: String -> MetaValue -> M.Map String MetaValue
923 -> M.Map String MetaValue
924addMetadata k v m = case M.lookup k m of
925 Nothing -> M.insert k v m
926 Just (MetaList xs) -> M.insert k
927 (MetaList (xs ++ [v])) m
928 Just x -> M.insert k (MetaList [v, x]) m
930readMetaValue :: String -> MetaValue
931readMetaValue s = case decode (UTF8.fromString s) of
932 Just (Yaml.String t) -> MetaString $ T.unpack t
933 Just (Yaml.Bool b) -> MetaBool b
936-- Returns usage message
937usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
938usageMessage programName = usageInfo
939 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
940 wrapWords 16 78 readers'names ++
941 '\n' : replicate 16 ' ' ++
942 "[ *only Pandoc's JSON version of native AST]" ++ "\nOutput formats: " ++
943 wrapWords 16 78 writers'names ++
944 '\n' : replicate 16 ' ' ++
945 "[**for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
947 writers'names = sort $ "json*" : "pdf**" : delete "json" (map fst writers)
948 readers'names = sort $ "json*" : delete "json" (map fst readers)
950-- Determine default reader based on source file extensions
951defaultReaderName :: String -> [FilePath] -> String
952defaultReaderName fallback [] = fallback
953defaultReaderName fallback (x:xs) =
954 case takeExtension (map toLower x) of
963 ".lhs" -> "markdown+lhs"
966 ".wiki" -> "mediawiki"
967 ".dokuwiki" -> "dokuwiki"
968 ".textile" -> "textile"
969 ".native" -> "native"
975 ".pdf" -> "pdf" -- so we get an "unknown reader" error
976 ".doc" -> "doc" -- so we get an "unknown reader" error
977 _ -> defaultReaderName fallback xs
979-- Returns True if extension of first source is .lhs
980lhsExtension :: [FilePath] -> Bool
981lhsExtension (x:_) = takeExtension x == ".lhs"
982lhsExtension _ = False
984-- Determine default writer based on output file extension
985defaultWriterName :: FilePath -> String
986defaultWriterName "-" = "html" -- no output file
988 case takeExtension (map toLower x) of
989 "" -> "markdown" -- empty extension
993 ".context" -> "context"
998 ".native" -> "native"
1000 ".txt" -> "markdown"
1001 ".text" -> "markdown"
1003 ".markdown" -> "markdown"
1004 ".textile" -> "textile"
1005 ".lhs" -> "markdown+lhs"
1006 ".texi" -> "texinfo"
1007 ".texinfo" -> "texinfo"
1013 ".asciidoc" -> "asciidoc"
1014 ".adoc" -> "asciidoc"
1021 ['.',y] | y `elem` ['1'..'9'] -> "man"
1024-- Transformations of a Pandoc document post-parsing:
1026extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1027extractMedia media dir d =
1028 case [fp | (fp, _, _) <- mediaDirectory media] of
1031 extractMediaBag True dir media
1032 return $ walk (adjustImagePath dir fps) d
1034adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1035adjustImagePath dir paths (Image attr lab (src, tit))
1036 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1037adjustImagePath _ _ x = x
1039adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1040adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1042applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1043applyTransforms transforms d = return $ foldr ($) d transforms
1045applyFilters :: [FilePath] -> [String] -> Pandoc -> IO Pandoc
1046applyFilters filters args d =
1047 foldrM ($) d $ map (flip externalFilter args) filters
1049uppercaseFirstLetter :: String -> String
1050uppercaseFirstLetter (c:cs) = toUpper c : cs
1051uppercaseFirstLetter [] = []
1056 rawArgs <- map UTF8.decodeArg <$> getArgs
1059 let (actions, args, errors) = getOpt Permute options rawArgs
1061 unless (null errors) $
1062 err 2 $ concat $ errors ++
1063 ["Try " ++ prg ++ " --help for more information."]
1065 -- thread option data structure through all supplied option actions
1066 opts <- foldl (>>=) (return defaultOpts) actions
1067 convertWithOpts opts args
1069convertWithOpts :: Opt -> [FilePath] -> IO ()
1070convertWithOpts opts args = do
1071 let Opt { optTabStop = tabStop
1072 , optPreserveTabs = preserveTabs
1073 , optStandalone = standalone
1074 , optReader = readerName
1075 , optWriter = writerName
1076 , optParseRaw = parseRaw
1077 , optVariables = variables
1078 , optMetadata = metadata
1079 , optTableOfContents = toc
1080 , optTransforms = transforms
1081 , optTemplate = templatePath
1082 , optOutputFile = outputFile
1083 , optNumberSections = numberSections
1084 , optNumberOffset = numberFrom
1085 , optSectionDivs = sectionDivs
1086 , optIncremental = incremental
1087 , optSelfContained = selfContained
1089 , optOldDashes = oldDashes
1091 , optHtmlQTags = htmlQTags
1092 , optHighlight = highlight
1093 , optHighlightStyle = highlightStyle
1094 , optChapters = chapters
1095 , optHTMLMathMethod = mathMethod'
1096 , optReferenceODT = referenceODT
1097 , optReferenceDocx = referenceDocx
1098 , optEpubStylesheet = epubStylesheet
1099 , optEpubMetadata = epubMetadata
1100 , optEpubFonts = epubFonts
1101 , optEpubChapterLevel = epubChapterLevel
1102 , optTOCDepth = epubTOCDepth
1103 , optDumpArgs = dumpArgs
1104 , optIgnoreArgs = ignoreArgs
1105 , optVerbose = verbose
1106 , optReferenceLinks = referenceLinks
1108 , optWrapText = wrap
1109 , optColumns = columns
1110 , optFilters = filters
1111 , optEmailObfuscation = obfuscationMethod
1112 , optIdentifierPrefix = idPrefix
1113 , optIndentedCodeClasses = codeBlockClasses
1114 , optDataDir = mbDataDir
1115 , optCiteMethod = citeMethod
1116 , optListings = listings
1117 , optLaTeXEngine = latexEngine
1118 , optLaTeXEngineArgs = latexEngineArgs
1119 , optSlideLevel = slideLevel
1120 , optSetextHeaders = setextHeaders
1122 , optTeXLigatures = texLigatures
1123 , optDefaultImageExtension = defaultImageExtension
1124 , optExtractMedia = mbExtractMedia
1126 , optTrackChanges = trackChanges
1127 , optFileScope = fileScope
1128 , optKaTeXStylesheet = katexStylesheet
1129 , optKaTeXJS = katexJS
1133 do UTF8.hPutStrLn stdout outputFile
1134 mapM_ (UTF8.hPutStrLn stdout) args
1137 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css"
1139 case (katexJS, katexStylesheet) of
1140 (Nothing, _) -> mathMethod'
1141 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1144 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1145 let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1146 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1147 "pandoc-citeproc" `notElem` map takeBaseName filters
1148 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1151 let sources = if ignoreArgs then [] else args
1153 datadir <- case mbDataDir of
1155 (Just <$> getAppUserDataDirectory "pandoc")
1156 (\e -> let _ = (e :: E.SomeException)
1158 Just _ -> return mbDataDir
1160 -- assign reader and writer based on options and filenames
1161 let readerName' = case map toLower readerName of
1162 [] -> defaultReaderName
1163 (if any isURI sources
1165 else "markdown") sources
1169 let writerName' = case map toLower writerName of
1170 [] -> defaultWriterName outputFile
1174 let format = takeWhile (`notElem` ['+','-'])
1175 $ takeFileName writerName' -- in case path to lua script
1177 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1179 let laTeXOutput = format `elem` ["latex", "beamer"]
1180 let conTeXtOutput = format == "context"
1181 let html5Output = format == "html5"
1183 let laTeXInput = "latex" `isPrefixOf` readerName' ||
1184 "beamer" `isPrefixOf` readerName'
1186 writer <- if ".lua" `isSuffixOf` format
1187 -- note: use non-lowercased version writerName
1188 then return $ IOStringWriter $ writeCustom writerName
1189 else case getWriter writerName' of
1193 "\nTo create a pdf with pandoc, use " ++
1194 "the latex or beamer writer and specify\n" ++
1195 "an output file with .pdf extension " ++
1196 "(pandoc -t latex -o filename.pdf)."
1200 reader <- if "t2t" == readerName'
1201 then (mkStringReader .
1203 getT2TMeta sources outputFile
1204 else case getReader readerName' of
1207 where e' = case readerName' of
1209 "\nPandoc can convert to PDF, but not from PDF."
1211 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1214 let standalone' = standalone || not (isTextFormat format) || pdfOutput
1216 templ <- case templatePath of
1217 _ | not standalone' -> return ""
1219 deftemp <- getDefaultTemplate datadir format
1224 -- strip off extensions
1225 let tp' = case takeExtension tp of
1228 E.catch (UTF8.readFile tp')
1229 (\e -> if isDoesNotExistError e
1231 (readDataFileUTF8 datadir
1232 ("templates" </> tp'))
1233 (\e' -> let _ = (e' :: E.SomeException)
1237 variables' <- case mathMethod of
1238 LaTeXMathML Nothing -> do
1239 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1240 return $ ("mathml-script", s) : variables
1241 MathML Nothing -> do
1242 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1243 return $ ("mathml-script", s) : variables
1244 _ -> return variables
1246 variables'' <- if format == "dzslides"
1248 dztempl <- readDataFileUTF8 datadir
1249 ("dzslides" </> "template.html")
1250 let dzline = "<!-- {{{{ dzslides core"
1251 let dzcore = unlines
1252 $ dropWhile (not . (dzline `isPrefixOf`))
1254 return $ ("dzslides-core", dzcore) : variables'
1255 else return variables'
1257 let sourceURL = case sources of
1259 (x:_) -> case parseURI x of
1261 | uriScheme u `elem` ["http:","https:"] ->
1262 Just $ show u{ uriQuery = "",
1266 let readerOpts = def{ readerSmart = if laTeXInput
1268 else smart || (texLigatures &&
1269 (laTeXOutput || conTeXtOutput))
1270 , readerStandalone = standalone'
1271 , readerParseRaw = parseRaw
1272 , readerColumns = columns
1273 , readerTabStop = tabStop
1274 , readerOldDashes = oldDashes
1275 , readerIndentedCodeClasses = codeBlockClasses
1276 , readerApplyMacros = not laTeXOutput
1277 , readerDefaultImageExtension = defaultImageExtension
1278 , readerTrace = trace
1279 , readerTrackChanges = trackChanges
1280 , readerFileScope = fileScope
1283 when (not (isTextFormat format) && outputFile == "-") $
1284 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1285 "Specify an output file using the -o option."
1287 let readSources [] = mapM readSource ["-"]
1288 readSources srcs = mapM readSource srcs
1289 readSource "-" = UTF8.getContents
1290 readSource src = case parseURI src of
1291 Just u | uriScheme u `elem` ["http:","https:"] ->
1293 _ -> UTF8.readFile src
1298 Right (bs,_) -> return $ UTF8.toString bs
1300 let readFiles [] = error "Cannot read archive from stdin"
1301 readFiles [x] = B.readFile x
1302 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1304 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1308 let handleIncludes' :: String -> IO (Either PandocError String)
1309 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1313 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1314 sourceToDoc sources' = fmap handleError $
1317 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1318 doc <- handleIncludes' srcs
1319 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1320 ByteStringReader r -> readFiles sources' >>= r readerOpts
1322 -- We parse first if (1) fileScope is set, (2), it's a binary
1323 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1324 -- of negatives as opposed to an OR of positives, so we do default
1325 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1327 (doc, media) <- case reader of
1328 (StringReader _) | not fileScope && readerName' /= "json" ->
1330 _ | null sources -> sourceToDoc sources
1331 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1332 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1334 let writerOptions = def { writerStandalone = standalone',
1335 writerTemplate = templ,
1336 writerVariables = variables'',
1337 writerTabStop = tabStop,
1338 writerTableOfContents = toc,
1339 writerHTMLMathMethod = mathMethod,
1340 writerIncremental = incremental,
1341 writerCiteMethod = citeMethod,
1342 writerIgnoreNotes = False,
1343 writerNumberSections = numberSections,
1344 writerNumberOffset = numberFrom,
1345 writerSectionDivs = sectionDivs,
1346 writerReferenceLinks = referenceLinks,
1348 writerWrapText = wrap,
1349 writerColumns = columns,
1350 writerEmailObfuscation = obfuscationMethod,
1351 writerIdentifierPrefix = idPrefix,
1352 writerSourceURL = sourceURL,
1353 writerUserDataDir = datadir,
1354 writerHtml5 = html5,
1355 writerHtmlQTags = htmlQTags,
1356 writerChapters = chapters,
1357 writerListings = listings,
1358 writerBeamer = False,
1359 writerSlideLevel = slideLevel,
1360 writerHighlight = highlight,
1361 writerHighlightStyle = highlightStyle,
1362 writerSetextHeaders = setextHeaders,
1363 writerTeXLigatures = texLigatures,
1364 writerEpubMetadata = epubMetadata,
1365 writerEpubStylesheet = epubStylesheet,
1366 writerEpubFonts = epubFonts,
1367 writerEpubChapterLevel = epubChapterLevel,
1368 writerTOCDepth = epubTOCDepth,
1369 writerReferenceODT = referenceODT,
1370 writerReferenceDocx = referenceDocx,
1371 writerMediaBag = media,
1372 writerVerbose = verbose,
1373 writerLaTeXArgs = latexEngineArgs
1377 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1378 adjustMetadata metadata >=>
1379 applyTransforms transforms >=>
1380 applyFilters filters' [format]) doc
1382 let writeBinary :: B.ByteString -> IO ()
1383 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1385 let writerFn :: FilePath -> String -> IO ()
1386 writerFn "-" = UTF8.putStr
1387 writerFn f = UTF8.writeFile f
1390 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1391 IOByteStringWriter f -> f writerOptions doc' >>= writeBinary
1394 -- make sure writer is latex or beamer or context or html5
1395 unless (laTeXOutput || conTeXtOutput || html5Output) $
1396 err 47 $ "cannot produce pdf output with " ++ format ++
1399 let pdfprog = case () of
1400 _ | conTeXtOutput -> "context"
1401 _ | html5Output -> "wkhtmltopdf"
1403 -- check for pdf creating program
1404 mbPdfProg <- findExecutable pdfprog
1405 when (isNothing mbPdfProg) $
1406 err 41 $ pdfprog ++ " not found. " ++
1407 pdfprog ++ " is needed for pdf output."
1409 res <- makePDF pdfprog f writerOptions doc'
1411 Right pdf -> writeBinary pdf
1413 B.hPutStr stderr err'
1414 B.hPut stderr $ B.pack [10]
1415 err 43 "Error producing PDF"
1416 | otherwise -> selfcontain (f writerOptions doc' ++
1417 ['\n' | not standalone'])
1418 >>= writerFn outputFile . handleEntities
1419 where htmlFormat = format `elem`
1420 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1421 selfcontain = if selfContained && htmlFormat
1422 then makeSelfContained writerOptions
1424 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