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 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')
142 ".js" -> ("node", f:args')
144 else err 85 $ "Filter " ++ f ++ " not found"
146 mbExe <- findExecutable f'
147 when (isNothing mbExe) $
148 err 83 $ "Error running filter " ++ f ++ "\n" ++
149 show f' ++ " not found in path."
150 (exitcode, outbs, errbs) <- E.handle filterException $
151 pipeProcess Nothing f' args'' $ encode d
152 unless (B.null errbs) $ B.hPutStr stderr errbs
154 ExitSuccess -> return $ either error id $ eitherDecode' outbs
155 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
156 "Filter returned error status " ++ show ec
157 where filterException :: E.SomeException -> IO a
158 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
161-- | Data structure for command line options.
163 { optTabStop :: Int -- ^ Number of spaces per tab
164 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
165 , optStandalone :: Bool -- ^ Include header, footer
166 , optReader :: String -- ^ Reader format
167 , optWriter :: String -- ^ Writer format
168 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
169 , optTableOfContents :: Bool -- ^ Include table of contents
170 , optTransforms :: [Transform] -- ^ Doc transforms to apply
171 , optTemplate :: Maybe FilePath -- ^ Custom template
172 , optVariables :: [(String,String)] -- ^ Template variables to set
173 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
174 , optOutputFile :: String -- ^ Name of output file
175 , optNumberSections :: Bool -- ^ Number sections in LaTeX
176 , optNumberOffset :: [Int] -- ^ Starting number for sections
177 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
178 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
179 , optSelfContained :: Bool -- ^ Make HTML accessible offline
180 , optSmart :: Bool -- ^ Use smart typography
181 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
182 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
183 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
184 , optHighlight :: Bool -- ^ Highlight source code
185 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
186 , optChapters :: Bool -- ^ Use chapter for top-level sects
187 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
188 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
189 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
190 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
191 , optEpubMetadata :: String -- ^ EPUB metadata
192 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
193 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
194 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
195 , optDumpArgs :: Bool -- ^ Output command-line arguments
196 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
197 , optVerbose :: Bool -- ^ Verbose diagnostic output
198 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
199 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
200 , optDpi :: Int -- ^ Dpi
201 , optWrapText :: WrapOption -- ^ Options for wrapping text
202 , optColumns :: Int -- ^ Line length in characters
203 , optFilters :: [FilePath] -- ^ Filters to apply
204 , optEmailObfuscation :: ObfuscationMethod
205 , optIdentifierPrefix :: String
206 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
207 , optDataDir :: Maybe FilePath
208 , optCiteMethod :: CiteMethod -- ^ Method to output cites
209 , optListings :: Bool -- ^ Use listings package for code blocks
210 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
211 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
212 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
213 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
214 , optAscii :: Bool -- ^ Use ascii characters only in html
215 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
216 , optDefaultImageExtension :: String -- ^ Default image extension
217 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
218 , optTrace :: Bool -- ^ Print debug information
219 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
220 , optFileScope :: Bool -- ^ Parse input files before combining
221 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
222 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
225-- | Defaults for command-line options.
229 , optPreserveTabs = False
230 , optStandalone = False
231 , optReader = "" -- null for default reader
232 , optWriter = "" -- null for default writer
233 , optParseRaw = False
234 , optTableOfContents = False
236 , optTemplate = Nothing
238 , optMetadata = M.empty
239 , optOutputFile = "-" -- "-" means stdout
240 , optNumberSections = False
241 , optNumberOffset = [0,0,0,0,0,0]
242 , optSectionDivs = False
243 , optIncremental = False
244 , optSelfContained = False
246 , optOldDashes = False
248 , optHtmlQTags = False
249 , optHighlight = True
250 , optHighlightStyle = pygments
251 , optChapters = False
252 , optHTMLMathMethod = PlainMath
253 , optReferenceODT = Nothing
254 , optReferenceDocx = Nothing
255 , optEpubStylesheet = Nothing
256 , optEpubMetadata = ""
258 , optEpubChapterLevel = 1
260 , optDumpArgs = False
261 , optIgnoreArgs = False
263 , optReferenceLinks = False
264 , optReferenceLocation = EndOfDocument
266 , optWrapText = WrapAuto
269 , optEmailObfuscation = NoObfuscation
270 , optIdentifierPrefix = ""
271 , optIndentedCodeClasses = []
272 , optDataDir = Nothing
273 , optCiteMethod = Citeproc
274 , optListings = False
275 , optLaTeXEngine = "pdflatex"
276 , optLaTeXEngineArgs = []
277 , optSlideLevel = Nothing
278 , optSetextHeaders = True
280 , optTeXLigatures = True
281 , optDefaultImageExtension = ""
282 , optExtractMedia = Nothing
284 , optTrackChanges = AcceptChanges
285 , optFileScope = False
286 , optKaTeXStylesheet = Nothing
287 , optKaTeXJS = Nothing
290-- | A list of functions, each transforming the options data structure
291-- in response to a command-line option.
292options :: [OptDescr (Opt -> IO Opt)]
294 [ Option "fr" ["from","read"]
296 (\arg opt -> return opt { optReader = arg })
300 , Option "tw" ["to","write"]
302 (\arg opt -> return opt { optWriter = arg })
306 , Option "o" ["output"]
308 (\arg opt -> return opt { optOutputFile = arg })
310 "" -- "Name of output file"
312 , Option "" ["data-dir"]
314 (\arg opt -> return opt { optDataDir = Just arg })
315 "DIRECTORY") -- "Directory containing pandoc data files."
318 , Option "R" ["parse-raw"]
320 (\opt -> return opt { optParseRaw = True }))
321 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
323 , Option "S" ["smart"]
325 (\opt -> return opt { optSmart = True }))
326 "" -- "Use smart quotes, dashes, and ellipses"
328 , Option "" ["old-dashes"]
330 (\opt -> return opt { optSmart = True
331 , optOldDashes = True }))
332 "" -- "Use smart quotes, dashes, and ellipses"
334 , Option "" ["base-header-level"]
339 let oldTransforms = optTransforms opt
341 return opt{ optTransforms =
342 headerShift shift : oldTransforms }
344 "base-header-level must be a number > 0")
346 "" -- "Headers base level"
348 , Option "" ["indented-code-classes"]
350 (\arg opt -> return opt { optIndentedCodeClasses = words $
351 map (\c -> if c == ',' then ' ' else c) arg })
353 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
355 , Option "F" ["filter"]
357 (\arg opt -> return opt { optFilters = arg : optFilters opt })
359 "" -- "External JSON filter"
361 , Option "" ["normalize"]
363 (\opt -> return opt { optTransforms =
364 normalize : optTransforms opt } ))
365 "" -- "Normalize the Pandoc AST"
367 , Option "p" ["preserve-tabs"]
369 (\opt -> return opt { optPreserveTabs = True }))
370 "" -- "Preserve tabs instead of converting to spaces"
372 , Option "" ["tab-stop"]
376 Just t | t > 0 -> return opt { optTabStop = t }
378 "tab-stop must be a number greater than 0")
380 "" -- "Tab stop (default 4)"
382 , Option "" ["track-changes"]
385 action <- case arg of
386 "accept" -> return AcceptChanges
387 "reject" -> return RejectChanges
388 "all" -> return AllChanges
390 ("Unknown option for track-changes: " ++ arg)
391 return opt { optTrackChanges = action })
393 "" -- "Accepting or reject MS Word track-changes.""
395 , Option "" ["file-scope"]
397 (\opt -> return opt { optFileScope = True }))
398 "" -- "Parse input files before combining"
400 , Option "" ["extract-media"]
403 return opt { optExtractMedia = Just arg })
405 "" -- "Directory to which to extract embedded media"
407 , Option "s" ["standalone"]
409 (\opt -> return opt { optStandalone = True }))
410 "" -- "Include needed header and footer on output"
412 , Option "" ["template"]
415 return opt{ optTemplate = Just arg,
416 optStandalone = True })
418 "" -- "Use custom template"
420 , Option "M" ["metadata"]
423 let (key,val) = case break (`elem` ":=") arg of
424 (k,_:v) -> (k, readMetaValue v)
425 (k,_) -> (k, MetaBool True)
426 return opt{ optMetadata = addMetadata key val
431 , Option "V" ["variable"]
434 let (key,val) = case break (`elem` ":=") arg of
437 return opt{ optVariables = (key,val) : optVariables opt })
441 , Option "D" ["print-default-template"]
444 templ <- getDefaultTemplate Nothing arg
446 Right t -> UTF8.hPutStr stdout t
447 Left e -> error $ show e
450 "" -- "Print default template for FORMAT"
452 , Option "" ["print-default-data-file"]
455 readDataFile Nothing arg >>= BS.hPutStr stdout
458 "" -- "Print default data file"
464 Just t | t > 0 -> return opt { optDpi = t }
466 "dpi must be a number greater than 0")
468 "" -- "Dpi (default 96)"
470 , Option "" ["no-wrap"]
472 (\opt -> do warn $ "--no-wrap is deprecated. " ++
473 "Use --wrap=none or --wrap=preserve instead."
474 return opt { optWrapText = WrapNone }))
480 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
481 Just o -> return opt { optWrapText = o }
482 Nothing -> err 77 "--wrap must be auto, none, or preserve")
483 "[auto|none|preserve]")
484 "" -- "Option for wrapping text in output"
486 , Option "" ["columns"]
490 Just t | t > 0 -> return opt { optColumns = t }
492 "columns must be a number greater than 0")
494 "" -- "Length of line in characters"
496 , Option "" ["toc", "table-of-contents"]
498 (\opt -> return opt { optTableOfContents = True }))
499 "" -- "Include table of contents"
501 , Option "" ["toc-depth"]
505 Just t | t >= 1 && t <= 6 ->
506 return opt { optTOCDepth = t }
508 "TOC level must be a number between 1 and 6")
510 "" -- "Number of levels to include in TOC"
512 , Option "" ["no-highlight"]
514 (\opt -> return opt { optHighlight = False }))
515 "" -- "Don't highlight source code"
517 , Option "" ["highlight-style"]
520 newStyle <- case map toLower arg of
521 "pygments" -> return pygments
522 "tango" -> return tango
523 "espresso" -> return espresso
524 "zenburn" -> return zenburn
525 "kate" -> return kate
526 "monochrome" -> return monochrome
527 "haddock" -> return haddock
529 "Unknown style :" ++ arg
530 return opt{ optHighlightStyle = newStyle })
532 "" -- "Style for highlighted code"
534 , Option "H" ["include-in-header"]
537 text <- UTF8.readFile arg
538 -- add new ones to end, so they're included in order specified
539 let newvars = optVariables opt ++ [("header-includes",text)]
540 return opt { optVariables = newvars,
541 optStandalone = True })
543 "" -- "File to include at end of header (implies -s)"
545 , Option "B" ["include-before-body"]
548 text <- UTF8.readFile arg
549 -- add new ones to end, so they're included in order specified
550 let newvars = optVariables opt ++ [("include-before",text)]
551 return opt { optVariables = newvars,
552 optStandalone = True })
554 "" -- "File to include before document body"
556 , Option "A" ["include-after-body"]
559 text <- UTF8.readFile arg
560 -- add new ones to end, so they're included in order specified
561 let newvars = optVariables opt ++ [("include-after",text)]
562 return opt { optVariables = newvars,
563 optStandalone = True })
565 "" -- "File to include after document body"
567 , Option "" ["self-contained"]
569 (\opt -> return opt { optSelfContained = True,
570 optStandalone = True }))
571 "" -- "Make slide shows include all the needed js and css"
573 , Option "" ["html-q-tags"]
576 return opt { optHtmlQTags = True }))
577 "" -- "Use <q> tags for quotes in HTML"
579 , Option "" ["ascii"]
581 (\opt -> return opt { optAscii = True }))
582 "" -- "Use ascii characters only in HTML output"
584 , Option "" ["reference-links"]
586 (\opt -> return opt { optReferenceLinks = True } ))
587 "" -- "Use reference links in parsing HTML"
589 , Option "" ["reference-location"]
592 action <- case arg of
593 "block" -> return EndOfBlock
594 "section" -> return EndOfSection
595 "document" -> return EndOfDocument
597 ("Unknown option for reference-location: " ++ arg)
598 return opt { optReferenceLocation = action })
599 "block|section|document")
600 "" -- "Accepting or reject MS Word track-changes.""
602 , Option "" ["atx-headers"]
604 (\opt -> return opt { optSetextHeaders = False } ))
605 "" -- "Use atx-style headers for markdown"
607 , Option "" ["chapters"]
609 (\opt -> return opt { optChapters = True }))
610 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
612 , Option "N" ["number-sections"]
614 (\opt -> return opt { optNumberSections = True }))
615 "" -- "Number sections in LaTeX"
617 , Option "" ["number-offset"]
620 case safeRead ('[':arg ++ "]") of
621 Just ns -> return opt { optNumberOffset = ns,
622 optNumberSections = True }
623 _ -> err 57 "could not parse number-offset")
625 "" -- "Starting number for sections, subsections, etc."
627 , Option "" ["no-tex-ligatures"]
629 (\opt -> return opt { optTeXLigatures = False }))
630 "" -- "Don't use tex ligatures for quotes, dashes"
632 , Option "" ["listings"]
634 (\opt -> return opt { optListings = True }))
635 "" -- "Use listings package for LaTeX code blocks"
637 , Option "i" ["incremental"]
639 (\opt -> return opt { optIncremental = True }))
640 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
642 , Option "" ["slide-level"]
646 Just t | t >= 1 && t <= 6 ->
647 return opt { optSlideLevel = Just t }
649 "slide level must be a number between 1 and 6")
651 "" -- "Force header level for slides"
653 , Option "" ["section-divs"]
655 (\opt -> return opt { optSectionDivs = True }))
656 "" -- "Put sections in div tags in HTML"
658 , Option "" ["default-image-extension"]
660 (\arg opt -> return opt { optDefaultImageExtension = arg })
662 "" -- "Default extension for extensionless images"
664 , Option "" ["email-obfuscation"]
667 method <- case arg of
668 "references" -> return ReferenceObfuscation
669 "javascript" -> return JavascriptObfuscation
670 "none" -> return NoObfuscation
672 ("Unknown obfuscation method: " ++ arg)
673 return opt { optEmailObfuscation = method })
674 "none|javascript|references")
675 "" -- "Method for obfuscating email in HTML"
677 , Option "" ["id-prefix"]
679 (\arg opt -> return opt { optIdentifierPrefix = arg })
681 "" -- "Prefix to add to automatically generated HTML identifiers"
683 , Option "T" ["title-prefix"]
686 let newvars = ("title-prefix", arg) : optVariables opt
687 return opt { optVariables = newvars,
688 optStandalone = True })
690 "" -- "String to prefix to HTML window title"
695 -- add new link to end, so it is included in proper order
696 let newvars = optVariables opt ++ [("css",arg)]
697 return opt { optVariables = newvars,
698 optStandalone = True })
700 "" -- "Link to CSS style sheet"
702 , Option "" ["reference-odt"]
705 return opt { optReferenceODT = Just arg })
707 "" -- "Path of custom reference.odt"
709 , Option "" ["reference-docx"]
712 return opt { optReferenceDocx = Just arg })
714 "" -- "Path of custom reference.docx"
716 , Option "" ["epub-stylesheet"]
719 text <- UTF8.readFile arg
720 return opt { optEpubStylesheet = Just text })
722 "" -- "Path of epub.css"
724 , Option "" ["epub-cover-image"]
727 return opt { optVariables =
728 ("epub-cover-image", arg) : optVariables opt })
730 "" -- "Path of epub cover image"
732 , Option "" ["epub-metadata"]
735 text <- UTF8.readFile arg
736 return opt { optEpubMetadata = text })
738 "" -- "Path of epub metadata file"
740 , Option "" ["epub-embed-font"]
743 return opt{ optEpubFonts = arg : optEpubFonts opt })
745 "" -- "Directory of fonts to embed"
747 , Option "" ["epub-chapter-level"]
751 Just t | t >= 1 && t <= 6 ->
752 return opt { optEpubChapterLevel = t }
754 "chapter level must be a number between 1 and 6")
756 "" -- "Header level at which to split chapters in EPUB"
758 , Option "" ["latex-engine"]
761 let b = takeBaseName arg
762 if b `elem` ["pdflatex", "lualatex", "xelatex"]
763 then return opt { optLaTeXEngine = arg }
764 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
766 "" -- "Name of latex program to use in generating PDF"
768 , Option "" ["latex-engine-opt"]
771 let oldArgs = optLaTeXEngineArgs opt
772 return opt { optLaTeXEngineArgs = arg : oldArgs })
774 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
776 , Option "" ["bibliography"]
778 (\arg opt -> return opt{ optMetadata = addMetadata
779 "bibliography" (readMetaValue arg)
788 return opt{ optMetadata = addMetadata "csl"
794 , Option "" ["citation-abbreviations"]
797 return opt{ optMetadata = addMetadata
798 "citation-abbreviations"
804 , Option "" ["natbib"]
806 (\opt -> return opt { optCiteMethod = Natbib }))
807 "" -- "Use natbib cite commands in LaTeX output"
809 , Option "" ["biblatex"]
811 (\opt -> return opt { optCiteMethod = Biblatex }))
812 "" -- "Use biblatex cite commands in LaTeX output"
814 , Option "m" ["latexmathml", "asciimathml"]
817 return opt { optHTMLMathMethod = LaTeXMathML arg })
819 "" -- "Use LaTeXMathML script in html output"
821 , Option "" ["mathml"]
824 return opt { optHTMLMathMethod = MathML arg })
826 "" -- "Use mathml for HTML math"
828 , Option "" ["mimetex"]
831 let url' = case arg of
833 Nothing -> "/cgi-bin/mimetex.cgi?"
834 return opt { optHTMLMathMethod = WebTeX url' })
836 "" -- "Use mimetex for HTML math"
838 , Option "" ["webtex"]
841 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
842 return opt { optHTMLMathMethod = WebTeX url' })
844 "" -- "Use web service for HTML math"
846 , Option "" ["jsmath"]
848 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
850 "" -- "Use jsMath for HTML math"
852 , Option "" ["mathjax"]
855 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
856 return opt { optHTMLMathMethod = MathJax url'})
858 "" -- "Use MathJax for HTML math"
859 , Option "" ["katex"]
864 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
866 "" -- Use KaTeX for HTML Math
868 , Option "" ["katex-stylesheet"]
871 return opt { optKaTeXStylesheet = Just arg })
873 "" -- Set the KaTeX Stylesheet location
875 , Option "" ["gladtex"]
877 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
878 "" -- "Use gladtex for HTML math"
880 , Option "" ["trace"]
882 (\opt -> return opt { optTrace = True }))
883 "" -- "Turn on diagnostic tracing in readers."
885 , Option "" ["dump-args"]
887 (\opt -> return opt { optDumpArgs = True }))
888 "" -- "Print output filename and arguments to stdout."
890 , Option "" ["ignore-args"]
892 (\opt -> return opt { optIgnoreArgs = True }))
893 "" -- "Ignore command-line arguments."
895 , Option "" ["verbose"]
897 (\opt -> return opt { optVerbose = True }))
898 "" -- "Verbose diagnostic output."
900 , Option "" ["bash-completion"]
904 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
905 let optnames (Option shorts longs _ _) =
906 map (\c -> ['-',c]) shorts ++
908 let allopts = unwords (concatMap optnames options)
909 UTF8.hPutStrLn stdout $ printf tpl allopts
910 (unwords (map fst readers))
911 (unwords ("pdf": map fst writers))
914 "" -- "Print bash completion script"
916 , Option "v" ["version"]
920 defaultDatadir <- getAppUserDataDirectory "pandoc"
921 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
922 compileInfo ++ "\nDefault user data directory: " ++
923 defaultDatadir ++ copyrightMessage)
925 "" -- "Print version"
927 , Option "h" ["help"]
931 UTF8.hPutStr stdout (usageMessage prg options)
938addMetadata :: String -> MetaValue -> M.Map String MetaValue
939 -> M.Map String MetaValue
940addMetadata k v m = case M.lookup k m of
941 Nothing -> M.insert k v m
942 Just (MetaList xs) -> M.insert k
943 (MetaList (xs ++ [v])) m
944 Just x -> M.insert k (MetaList [v, x]) m
946readMetaValue :: String -> MetaValue
947readMetaValue s = case decode (UTF8.fromString s) of
948 Just (Yaml.String t) -> MetaString $ T.unpack t
949 Just (Yaml.Bool b) -> MetaBool b
952-- Returns usage message
953usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
954usageMessage programName = usageInfo
955 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
956 wrapWords 16 78 readers'names ++
957 '\n' : replicate 16 ' ' ++
958 "[* only Pandoc's JSON version of native AST]" ++ "\nOutput formats: " ++
959 wrapWords 16 78 writers'names ++
960 '\n' : replicate 16 ' ' ++
961 "[** for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
963 writers'names = sort $ "json*" : "pdf**" : delete "json" (map fst writers)
964 readers'names = sort $ "json*" : delete "json" (map fst readers)
966-- Determine default reader based on source file extensions
967defaultReaderName :: String -> [FilePath] -> String
968defaultReaderName fallback [] = fallback
969defaultReaderName fallback (x:xs) =
970 case takeExtension (map toLower x) of
979 ".lhs" -> "markdown+lhs"
982 ".wiki" -> "mediawiki"
983 ".dokuwiki" -> "dokuwiki"
984 ".textile" -> "textile"
985 ".native" -> "native"
991 ".pdf" -> "pdf" -- so we get an "unknown reader" error
992 ".doc" -> "doc" -- so we get an "unknown reader" error
993 _ -> defaultReaderName fallback xs
995-- Returns True if extension of first source is .lhs
996lhsExtension :: [FilePath] -> Bool
997lhsExtension (x:_) = takeExtension x == ".lhs"
998lhsExtension _ = False
1000-- Determine default writer based on output file extension
1001defaultWriterName :: FilePath -> String
1002defaultWriterName "-" = "html" -- no output file
1003defaultWriterName x =
1004 case takeExtension (map toLower x) of
1005 "" -> "markdown" -- empty extension
1009 ".context" -> "context"
1014 ".native" -> "native"
1016 ".txt" -> "markdown"
1017 ".text" -> "markdown"
1019 ".markdown" -> "markdown"
1020 ".textile" -> "textile"
1021 ".lhs" -> "markdown+lhs"
1022 ".texi" -> "texinfo"
1023 ".texinfo" -> "texinfo"
1029 ".asciidoc" -> "asciidoc"
1030 ".adoc" -> "asciidoc"
1037 ['.',y] | y `elem` ['1'..'9'] -> "man"
1040-- Transformations of a Pandoc document post-parsing:
1042extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1043extractMedia media dir d =
1044 case [fp | (fp, _, _) <- mediaDirectory media] of
1047 extractMediaBag True dir media
1048 return $ walk (adjustImagePath dir fps) d
1050adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1051adjustImagePath dir paths (Image attr lab (src, tit))
1052 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1053adjustImagePath _ _ x = x
1055adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1056adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1058applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1059applyTransforms transforms d = return $ foldr ($) d transforms
1061 -- First we check to see if a filter is a path. If it isn't, we
1062 -- check to see whether it's in `userdir/filters`. If not, we leave
1064expandFilterPath :: Maybe FilePath -> FilePath -> IO FilePath
1065expandFilterPath mbDatadir fp
1066 | '/' `elem` fp = return fp
1067 | Just datadir <- mbDatadir = do
1068 let filterPath = (datadir </> "filters" </> fp)
1069 filterPathExists <- doesFileExist filterPath
1071 then return filterPath
1073 | otherwise = return fp
1075applyFilters :: Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> IO Pandoc
1076applyFilters mbDatadir filters args d = do
1077 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
1078 foldrM ($) d $ map (flip externalFilter args) expandedFilters
1080uppercaseFirstLetter :: String -> String
1081uppercaseFirstLetter (c:cs) = toUpper c : cs
1082uppercaseFirstLetter [] = []
1087 rawArgs <- map UTF8.decodeArg <$> getArgs
1090 let (actions, args, errors) = getOpt Permute options rawArgs
1092 unless (null errors) $
1093 err 2 $ concat $ errors ++
1094 ["Try " ++ prg ++ " --help for more information."]
1096 -- thread option data structure through all supplied option actions
1097 opts <- foldl (>>=) (return defaultOpts) actions
1098 convertWithOpts opts args
1100convertWithOpts :: Opt -> [FilePath] -> IO ()
1101convertWithOpts opts args = do
1102 let Opt { optTabStop = tabStop
1103 , optPreserveTabs = preserveTabs
1104 , optStandalone = standalone
1105 , optReader = readerName
1106 , optWriter = writerName
1107 , optParseRaw = parseRaw
1108 , optVariables = variables
1109 , optMetadata = metadata
1110 , optTableOfContents = toc
1111 , optTransforms = transforms
1112 , optTemplate = templatePath
1113 , optOutputFile = outputFile
1114 , optNumberSections = numberSections
1115 , optNumberOffset = numberFrom
1116 , optSectionDivs = sectionDivs
1117 , optIncremental = incremental
1118 , optSelfContained = selfContained
1120 , optOldDashes = oldDashes
1122 , optHtmlQTags = htmlQTags
1123 , optHighlight = highlight
1124 , optHighlightStyle = highlightStyle
1125 , optChapters = chapters
1126 , optHTMLMathMethod = mathMethod'
1127 , optReferenceODT = referenceODT
1128 , optReferenceDocx = referenceDocx
1129 , optEpubStylesheet = epubStylesheet
1130 , optEpubMetadata = epubMetadata
1131 , optEpubFonts = epubFonts
1132 , optEpubChapterLevel = epubChapterLevel
1133 , optTOCDepth = epubTOCDepth
1134 , optDumpArgs = dumpArgs
1135 , optIgnoreArgs = ignoreArgs
1136 , optVerbose = verbose
1137 , optReferenceLinks = referenceLinks
1138 , optReferenceLocation = referenceLocation
1140 , optWrapText = wrap
1141 , optColumns = columns
1142 , optFilters = filters
1143 , optEmailObfuscation = obfuscationMethod
1144 , optIdentifierPrefix = idPrefix
1145 , optIndentedCodeClasses = codeBlockClasses
1146 , optDataDir = mbDataDir
1147 , optCiteMethod = citeMethod
1148 , optListings = listings
1149 , optLaTeXEngine = latexEngine
1150 , optLaTeXEngineArgs = latexEngineArgs
1151 , optSlideLevel = slideLevel
1152 , optSetextHeaders = setextHeaders
1154 , optTeXLigatures = texLigatures
1155 , optDefaultImageExtension = defaultImageExtension
1156 , optExtractMedia = mbExtractMedia
1158 , optTrackChanges = trackChanges
1159 , optFileScope = fileScope
1160 , optKaTeXStylesheet = katexStylesheet
1161 , optKaTeXJS = katexJS
1165 do UTF8.hPutStrLn stdout outputFile
1166 mapM_ (UTF8.hPutStrLn stdout) args
1169 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
1171 case (katexJS, katexStylesheet) of
1172 (Nothing, _) -> mathMethod'
1173 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1176 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1177 let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1178 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1179 "pandoc-citeproc" `notElem` map takeBaseName filters
1180 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1183 let sources = if ignoreArgs then [] else args
1185 datadir <- case mbDataDir of
1187 (Just <$> getAppUserDataDirectory "pandoc")
1188 (\e -> let _ = (e :: E.SomeException)
1190 Just _ -> return mbDataDir
1192 -- assign reader and writer based on options and filenames
1193 let readerName' = case map toLower readerName of
1194 [] -> defaultReaderName
1195 (if any isURI sources
1197 else "markdown") sources
1201 let writerName' = case map toLower writerName of
1202 [] -> defaultWriterName outputFile
1206 let format = takeWhile (`notElem` ['+','-'])
1207 $ takeFileName writerName' -- in case path to lua script
1209 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1211 let laTeXOutput = format `elem` ["latex", "beamer"]
1212 let conTeXtOutput = format == "context"
1213 let html5Output = format == "html5"
1215 let laTeXInput = "latex" `isPrefixOf` readerName' ||
1216 "beamer" `isPrefixOf` readerName'
1218 writer <- if ".lua" `isSuffixOf` format
1219 -- note: use non-lowercased version writerName
1220 then return $ IOStringWriter $ writeCustom writerName
1221 else case getWriter writerName' of
1225 "\nTo create a pdf with pandoc, use " ++
1226 "the latex or beamer writer and specify\n" ++
1227 "an output file with .pdf extension " ++
1228 "(pandoc -t latex -o filename.pdf)."
1232 reader <- if "t2t" == readerName'
1233 then (mkStringReader .
1235 getT2TMeta sources outputFile
1236 else case getReader readerName' of
1239 where e' = case readerName' of
1241 "\nPandoc can convert to PDF, but not from PDF."
1243 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1246 let standalone' = standalone || not (isTextFormat format) || pdfOutput
1248 templ <- case templatePath of
1249 _ | not standalone' -> return ""
1251 deftemp <- getDefaultTemplate datadir format
1256 -- strip off extensions
1257 let tp' = case takeExtension tp of
1260 E.catch (UTF8.readFile tp')
1261 (\e -> if isDoesNotExistError e
1263 (readDataFileUTF8 datadir
1264 ("templates" </> tp'))
1265 (\e' -> let _ = (e' :: E.SomeException)
1269 variables' <- case mathMethod of
1270 LaTeXMathML Nothing -> do
1271 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1272 return $ ("mathml-script", s) : variables
1273 MathML Nothing -> do
1274 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1275 return $ ("mathml-script", s) : variables
1276 _ -> return variables
1278 variables'' <- if format == "dzslides"
1280 dztempl <- readDataFileUTF8 datadir
1281 ("dzslides" </> "template.html")
1282 let dzline = "<!-- {{{{ dzslides core"
1283 let dzcore = unlines
1284 $ dropWhile (not . (dzline `isPrefixOf`))
1286 return $ ("dzslides-core", dzcore) : variables'
1287 else return variables'
1289 let sourceURL = case sources of
1291 (x:_) -> case parseURI x of
1293 | uriScheme u `elem` ["http:","https:"] ->
1294 Just $ show u{ uriQuery = "",
1298 let readerOpts = def{ readerSmart = if laTeXInput
1300 else smart || (texLigatures &&
1301 (laTeXOutput || conTeXtOutput))
1302 , readerStandalone = standalone'
1303 , readerParseRaw = parseRaw
1304 , readerColumns = columns
1305 , readerTabStop = tabStop
1306 , readerOldDashes = oldDashes
1307 , readerIndentedCodeClasses = codeBlockClasses
1308 , readerApplyMacros = not laTeXOutput
1309 , readerDefaultImageExtension = defaultImageExtension
1310 , readerTrace = trace
1311 , readerTrackChanges = trackChanges
1312 , readerFileScope = fileScope
1315 when (not (isTextFormat format) && outputFile == "-") $
1316 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1317 "Specify an output file using the -o option."
1319 let readSources [] = mapM readSource ["-"]
1320 readSources srcs = mapM readSource srcs
1321 readSource "-" = UTF8.getContents
1322 readSource src = case parseURI src of
1323 Just u | uriScheme u `elem` ["http:","https:"] ->
1325 _ -> UTF8.readFile src
1330 Right (bs,_) -> return $ UTF8.toString bs
1332 let readFiles [] = error "Cannot read archive from stdin"
1333 readFiles [x] = B.readFile x
1334 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1336 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1340 let handleIncludes' :: String -> IO (Either PandocError String)
1341 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1345 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1346 sourceToDoc sources' = fmap handleError $
1349 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1350 doc <- handleIncludes' srcs
1351 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1352 ByteStringReader r -> readFiles sources' >>= r readerOpts
1354 -- We parse first if (1) fileScope is set, (2), it's a binary
1355 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1356 -- of negatives as opposed to an OR of positives, so we do default
1357 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1359 (doc, media) <- case reader of
1360 (StringReader _) | not fileScope && readerName' /= "json" ->
1362 _ | null sources -> sourceToDoc sources
1363 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1364 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1366 let writerOptions = def { writerStandalone = standalone',
1367 writerTemplate = templ,
1368 writerVariables = variables'',
1369 writerTabStop = tabStop,
1370 writerTableOfContents = toc,
1371 writerHTMLMathMethod = mathMethod,
1372 writerIncremental = incremental,
1373 writerCiteMethod = citeMethod,
1374 writerIgnoreNotes = False,
1375 writerNumberSections = numberSections,
1376 writerNumberOffset = numberFrom,
1377 writerSectionDivs = sectionDivs,
1378 writerReferenceLinks = referenceLinks,
1379 writerReferenceLocation = referenceLocation,
1381 writerWrapText = wrap,
1382 writerColumns = columns,
1383 writerEmailObfuscation = obfuscationMethod,
1384 writerIdentifierPrefix = idPrefix,
1385 writerSourceURL = sourceURL,
1386 writerUserDataDir = datadir,
1387 writerHtml5 = html5,
1388 writerHtmlQTags = htmlQTags,
1389 writerChapters = chapters,
1390 writerListings = listings,
1391 writerBeamer = False,
1392 writerSlideLevel = slideLevel,
1393 writerHighlight = highlight,
1394 writerHighlightStyle = highlightStyle,
1395 writerSetextHeaders = setextHeaders,
1396 writerTeXLigatures = texLigatures,
1397 writerEpubMetadata = epubMetadata,
1398 writerEpubStylesheet = epubStylesheet,
1399 writerEpubFonts = epubFonts,
1400 writerEpubChapterLevel = epubChapterLevel,
1401 writerTOCDepth = epubTOCDepth,
1402 writerReferenceODT = referenceODT,
1403 writerReferenceDocx = referenceDocx,
1404 writerMediaBag = media,
1405 writerVerbose = verbose,
1406 writerLaTeXArgs = latexEngineArgs
1410 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1411 adjustMetadata metadata >=>
1412 applyTransforms transforms >=>
1413 applyFilters datadir filters' [format]) doc
1415 let writeBinary :: B.ByteString -> IO ()
1416 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1418 let writerFn :: FilePath -> String -> IO ()
1419 writerFn "-" = UTF8.putStr
1420 writerFn f = UTF8.writeFile f
1423 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1424 IOByteStringWriter f -> f writerOptions doc' >>= writeBinary
1427 -- make sure writer is latex or beamer or context or html5
1428 unless (laTeXOutput || conTeXtOutput || html5Output) $
1429 err 47 $ "cannot produce pdf output with " ++ format ++
1432 let pdfprog = case () of
1433 _ | conTeXtOutput -> "context"
1434 _ | html5Output -> "wkhtmltopdf"
1436 -- check for pdf creating program
1437 mbPdfProg <- findExecutable pdfprog
1438 when (isNothing mbPdfProg) $
1439 err 41 $ pdfprog ++ " not found. " ++
1440 pdfprog ++ " is needed for pdf output."
1442 res <- makePDF pdfprog f writerOptions doc'
1444 Right pdf -> writeBinary pdf
1446 B.hPutStr stderr err'
1447 B.hPut stderr $ B.pack [10]
1448 err 43 "Error producing PDF"
1449 | otherwise -> selfcontain (f writerOptions doc' ++
1450 ['\n' | not standalone'])
1451 >>= writerFn outputFile . handleEntities
1452 where htmlFormat = format `elem`
1453 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1454 selfcontain = if selfContained && htmlFormat
1455 then makeSelfContained writerOptions
1457 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