1{-# LANGUAGE CPP, TupleSections #-}
3Copyright (C) 2006-2014 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-2014 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 ( exitWith, ExitCode (..) )
50import System.Console.GetOpt
51import Data.Char ( toLower )
52import Data.List ( 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 (isJust, fromMaybe)
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)
75type Transform = Pandoc -> Pandoc
77copyrightMessage :: String
78copyrightMessage = intercalate "\n" [
80 "Copyright (C) 2006-2014 John MacFarlane",
81 "Web: http://johnmacfarlane.net/pandoc",
82 "This is free software; see the source for copying conditions.",
83 "There is no warranty, not even for merchantability or fitness",
84 "for a particular purpose." ]
88 "\nCompiled with texmath " ++
89 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
90 ".\nSyntax highlighting is supported for the following languages:\n " ++
92 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
94-- | Converts a list of strings into a single string with the items printed as
95-- comma separated words in lines with a maximum line length.
96wrapWords :: Int -> Int -> [String] -> String
97wrapWords indent c = wrap' (c - indent) (c - indent)
100 wrap' cols remaining (x:xs)
101 | remaining == cols =
102 x ++ wrap' cols (remaining - length x) xs
103 | (length x + 1) > remaining =
104 ",\n" ++ replicate indent ' ' ++ x ++
105 wrap' cols (cols - length x) xs
108 wrap' cols (remaining - length x - 2) xs
110isTextFormat :: String -> Bool
111isTextFormat s = takeWhile (`notElem` "+-") s `notElem` binaries
112 where binaries = ["odt","docx","epub","epub3"]
114externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
115externalFilter f args' d = do
116 mbexe <- if '/' `elem` f
117 -- don't check PATH if filter name has a path
119 -- we catch isDoesNotExistError because this will
120 -- be triggered if PATH not set:
121 else E.catch (findExecutable f)
122 (\e -> if isDoesNotExistError e
125 (f', args'') <- case mbexe of
126 Just x -> return (x, args')
128 exists <- doesFileExist f
131 isExecutable <- executable `fmap`
134 case map toLower $ takeExtension f of
135 _ | isExecutable -> (f, args')
136 ".py" -> ("python", f:args')
137 ".hs" -> ("runhaskell", f:args')
138 ".pl" -> ("perl", f:args')
139 ".rb" -> ("ruby", f:args')
140 ".php" -> ("php", f:args')
142 else err 85 $ "Filter " ++ f ++ " not found"
143 (exitcode, outbs, errbs) <- E.handle filterException $
144 pipeProcess Nothing f' args'' $ encode d
145 when (not $ B.null errbs) $ B.hPutStr stderr errbs
147 ExitSuccess -> return $ either error id $ eitherDecode' outbs
148 ExitFailure _ -> err 83 $ "Error running filter " ++ f
149 where filterException :: E.SomeException -> IO a
150 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
153-- | Data structure for command line options.
155 { optTabStop :: Int -- ^ Number of spaces per tab
156 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
157 , optStandalone :: Bool -- ^ Include header, footer
158 , optReader :: String -- ^ Reader format
159 , optWriter :: String -- ^ Writer format
160 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
161 , optTableOfContents :: Bool -- ^ Include table of contents
162 , optTransforms :: [Transform] -- ^ Doc transforms to apply
163 , optTemplate :: Maybe FilePath -- ^ Custom template
164 , optVariables :: [(String,String)] -- ^ Template variables to set
165 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
166 , optOutputFile :: String -- ^ Name of output file
167 , optNumberSections :: Bool -- ^ Number sections in LaTeX
168 , optNumberOffset :: [Int] -- ^ Starting number for sections
169 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
170 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
171 , optSelfContained :: Bool -- ^ Make HTML accessible offline
172 , optSmart :: Bool -- ^ Use smart typography
173 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
174 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
175 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
176 , optHighlight :: Bool -- ^ Highlight source code
177 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
178 , optChapters :: Bool -- ^ Use chapter for top-level sects
179 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
180 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
181 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
182 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
183 , optEpubMetadata :: String -- ^ EPUB metadata
184 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
185 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
186 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
187 , optDumpArgs :: Bool -- ^ Output command-line arguments
188 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
189 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
190 , optWrapText :: Bool -- ^ Wrap text
191 , optColumns :: Int -- ^ Line length in characters
192 , optFilters :: [FilePath] -- ^ Filters to apply
193 , optEmailObfuscation :: ObfuscationMethod
194 , optIdentifierPrefix :: String
195 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
196 , optDataDir :: Maybe FilePath
197 , optCiteMethod :: CiteMethod -- ^ Method to output cites
198 , optListings :: Bool -- ^ Use listings package for code blocks
199 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
200 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
201 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
202 , optAscii :: Bool -- ^ Use ascii characters only in html
203 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
204 , optDefaultImageExtension :: String -- ^ Default image extension
205 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
206 , optTrace :: Bool -- ^ Print debug information
207 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
208 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
209 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
212-- | Defaults for command-line options.
216 , optPreserveTabs = False
217 , optStandalone = False
218 , optReader = "" -- null for default reader
219 , optWriter = "" -- null for default writer
220 , optParseRaw = False
221 , optTableOfContents = False
223 , optTemplate = Nothing
225 , optMetadata = M.empty
226 , optOutputFile = "-" -- "-" means stdout
227 , optNumberSections = False
228 , optNumberOffset = [0,0,0,0,0,0]
229 , optSectionDivs = False
230 , optIncremental = False
231 , optSelfContained = False
233 , optOldDashes = False
235 , optHtmlQTags = False
236 , optHighlight = True
237 , optHighlightStyle = pygments
238 , optChapters = False
239 , optHTMLMathMethod = PlainMath
240 , optReferenceODT = Nothing
241 , optReferenceDocx = Nothing
242 , optEpubStylesheet = Nothing
243 , optEpubMetadata = ""
245 , optEpubChapterLevel = 1
247 , optDumpArgs = False
248 , optIgnoreArgs = False
249 , optReferenceLinks = False
253 , optEmailObfuscation = JavascriptObfuscation
254 , optIdentifierPrefix = ""
255 , optIndentedCodeClasses = []
256 , optDataDir = Nothing
257 , optCiteMethod = Citeproc
258 , optListings = False
259 , optLaTeXEngine = "pdflatex"
260 , optSlideLevel = Nothing
261 , optSetextHeaders = True
263 , optTeXLigatures = True
264 , optDefaultImageExtension = ""
265 , optExtractMedia = Nothing
267 , optTrackChanges = AcceptChanges
268 , optKaTeXStylesheet = Nothing
269 , optKaTeXJS = Nothing
272-- | A list of functions, each transforming the options data structure
273-- in response to a command-line option.
274options :: [OptDescr (Opt -> IO Opt)]
276 [ Option "fr" ["from","read"]
278 (\arg opt -> return opt { optReader = arg })
282 , Option "tw" ["to","write"]
284 (\arg opt -> return opt { optWriter = arg })
288 , Option "o" ["output"]
290 (\arg opt -> return opt { optOutputFile = arg })
292 "" -- "Name of output file"
294 , Option "" ["data-dir"]
296 (\arg opt -> return opt { optDataDir = Just arg })
297 "DIRECTORY") -- "Directory containing pandoc data files."
300 , Option "" ["strict"]
303 err 59 $ "The --strict option has been removed.\n" ++
304 "Use `markdown_strict' input or output format instead."
306 "" -- "Disable markdown syntax extensions"
308 , Option "R" ["parse-raw"]
310 (\opt -> return opt { optParseRaw = True }))
311 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
313 , Option "S" ["smart"]
315 (\opt -> return opt { optSmart = True }))
316 "" -- "Use smart quotes, dashes, and ellipses"
318 , Option "" ["old-dashes"]
320 (\opt -> return opt { optSmart = True
321 , optOldDashes = True }))
322 "" -- "Use smart quotes, dashes, and ellipses"
324 , Option "" ["base-header-level"]
329 let oldTransforms = optTransforms opt
331 return opt{ optTransforms =
332 headerShift shift : oldTransforms }
334 "base-header-level must be a number > 0")
336 "" -- "Headers base level"
338 , Option "" ["indented-code-classes"]
340 (\arg opt -> return opt { optIndentedCodeClasses = words $
341 map (\c -> if c == ',' then ' ' else c) arg })
343 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
345 , Option "F" ["filter"]
347 (\arg opt -> return opt { optFilters = arg : optFilters opt })
349 "" -- "External JSON filter"
351 , Option "" ["normalize"]
353 (\opt -> return opt { optTransforms =
354 normalize : optTransforms opt } ))
355 "" -- "Normalize the Pandoc AST"
357 , Option "p" ["preserve-tabs"]
359 (\opt -> return opt { optPreserveTabs = True }))
360 "" -- "Preserve tabs instead of converting to spaces"
362 , Option "" ["tab-stop"]
366 Just t | t > 0 -> return opt { optTabStop = t }
368 "tab-stop must be a number greater than 0")
370 "" -- "Tab stop (default 4)"
372 , Option "" ["track-changes"]
375 action <- case arg of
376 "accept" -> return AcceptChanges
377 "reject" -> return RejectChanges
378 "all" -> return AllChanges
380 ("Unknown option for track-changes: " ++ arg)
381 return opt { optTrackChanges = action })
383 "" -- "Accepting or reject MS Word track-changes.""
385 , Option "" ["extract-media"]
388 return opt { optExtractMedia = Just arg })
390 "" -- "Directory to which to extract embedded media"
392 , Option "s" ["standalone"]
394 (\opt -> return opt { optStandalone = True }))
395 "" -- "Include needed header and footer on output"
397 , Option "" ["template"]
400 return opt{ optTemplate = Just arg,
401 optStandalone = True })
403 "" -- "Use custom template"
405 , Option "M" ["metadata"]
408 let (key,val) = case break (`elem` ":=") arg of
409 (k,_:v) -> (k, readMetaValue v)
410 (k,_) -> (k, MetaBool True)
411 return opt{ optMetadata = addMetadata key val
416 , Option "V" ["variable"]
419 let (key,val) = case break (`elem` ":=") arg of
422 return opt{ optVariables = (key,val) : optVariables opt })
426 , Option "D" ["print-default-template"]
429 templ <- getDefaultTemplate Nothing arg
431 Right t -> UTF8.hPutStr stdout t
432 Left e -> error $ show e
433 exitWith ExitSuccess)
435 "" -- "Print default template for FORMAT"
437 , Option "" ["print-default-data-file"]
440 readDataFile Nothing arg >>= BS.hPutStr stdout
441 exitWith ExitSuccess)
443 "" -- "Print default data file"
445 , Option "" ["no-wrap"]
447 (\opt -> return opt { optWrapText = False }))
448 "" -- "Do not wrap text in output"
450 , Option "" ["columns"]
454 Just t | t > 0 -> return opt { optColumns = t }
456 "columns must be a number greater than 0")
458 "" -- "Length of line in characters"
460 , Option "" ["toc", "table-of-contents"]
462 (\opt -> return opt { optTableOfContents = True }))
463 "" -- "Include table of contents"
465 , Option "" ["toc-depth"]
469 Just t | t >= 1 && t <= 6 ->
470 return opt { optTOCDepth = t }
472 "TOC level must be a number between 1 and 6")
474 "" -- "Number of levels to include in TOC"
476 , Option "" ["no-highlight"]
478 (\opt -> return opt { optHighlight = False }))
479 "" -- "Don't highlight source code"
481 , Option "" ["highlight-style"]
484 newStyle <- case map toLower arg of
485 "pygments" -> return pygments
486 "tango" -> return tango
487 "espresso" -> return espresso
488 "zenburn" -> return zenburn
489 "kate" -> return kate
490 "monochrome" -> return monochrome
491 "haddock" -> return haddock
493 "Unknown style :" ++ arg
494 return opt{ optHighlightStyle = newStyle })
496 "" -- "Style for highlighted code"
498 , Option "H" ["include-in-header"]
501 text <- UTF8.readFile arg
502 -- add new ones to end, so they're included in order specified
503 let newvars = optVariables opt ++ [("header-includes",text)]
504 return opt { optVariables = newvars,
505 optStandalone = True })
507 "" -- "File to include at end of header (implies -s)"
509 , Option "B" ["include-before-body"]
512 text <- UTF8.readFile arg
513 -- add new ones to end, so they're included in order specified
514 let newvars = optVariables opt ++ [("include-before",text)]
515 return opt { optVariables = newvars,
516 optStandalone = True })
518 "" -- "File to include before document body"
520 , Option "A" ["include-after-body"]
523 text <- UTF8.readFile arg
524 -- add new ones to end, so they're included in order specified
525 let newvars = optVariables opt ++ [("include-after",text)]
526 return opt { optVariables = newvars,
527 optStandalone = True })
529 "" -- "File to include after document body"
531 , Option "" ["self-contained"]
533 (\opt -> return opt { optSelfContained = True,
534 optStandalone = True }))
535 "" -- "Make slide shows include all the needed js and css"
537 , Option "" ["offline"]
539 (\opt -> do warn $ "--offline is deprecated. Use --self-contained instead."
540 return opt { optSelfContained = True,
541 optStandalone = True }))
542 "" -- "Make slide shows include all the needed js and css"
543 -- deprecated synonym for --self-contained
545 , Option "5" ["html5"]
548 warn $ "--html5 is deprecated. "
549 ++ "Use the html5 output format instead."
550 return opt { optHtml5 = True }))
551 "" -- "Produce HTML5 in HTML output"
553 , Option "" ["html-q-tags"]
556 return opt { optHtmlQTags = True }))
557 "" -- "Use <q> tags for quotes in HTML"
559 , Option "" ["ascii"]
561 (\opt -> return opt { optAscii = True }))
562 "" -- "Use ascii characters only in HTML output"
564 , Option "" ["reference-links"]
566 (\opt -> return opt { optReferenceLinks = True } ))
567 "" -- "Use reference links in parsing HTML"
569 , Option "" ["atx-headers"]
571 (\opt -> return opt { optSetextHeaders = False } ))
572 "" -- "Use atx-style headers for markdown"
574 , Option "" ["chapters"]
576 (\opt -> return opt { optChapters = True }))
577 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
579 , Option "N" ["number-sections"]
581 (\opt -> return opt { optNumberSections = True }))
582 "" -- "Number sections in LaTeX"
584 , Option "" ["number-offset"]
587 case safeRead ('[':arg ++ "]") of
588 Just ns -> return opt { optNumberOffset = ns,
589 optNumberSections = True }
590 _ -> err 57 "could not parse number-offset")
592 "" -- "Starting number for sections, subsections, etc."
594 , Option "" ["no-tex-ligatures"]
596 (\opt -> return opt { optTeXLigatures = False }))
597 "" -- "Don't use tex ligatures for quotes, dashes"
599 , Option "" ["listings"]
601 (\opt -> return opt { optListings = True }))
602 "" -- "Use listings package for LaTeX code blocks"
604 , Option "i" ["incremental"]
606 (\opt -> return opt { optIncremental = True }))
607 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
609 , Option "" ["slide-level"]
613 Just t | t >= 1 && t <= 6 ->
614 return opt { optSlideLevel = Just t }
616 "slide level must be a number between 1 and 6")
618 "" -- "Force header level for slides"
620 , Option "" ["section-divs"]
622 (\opt -> return opt { optSectionDivs = True }))
623 "" -- "Put sections in div tags in HTML"
625 , Option "" ["default-image-extension"]
627 (\arg opt -> return opt { optDefaultImageExtension = arg })
629 "" -- "Default extension for extensionless images"
631 , Option "" ["email-obfuscation"]
634 method <- case arg of
635 "references" -> return ReferenceObfuscation
636 "javascript" -> return JavascriptObfuscation
637 "none" -> return NoObfuscation
639 ("Unknown obfuscation method: " ++ arg)
640 return opt { optEmailObfuscation = method })
641 "none|javascript|references")
642 "" -- "Method for obfuscating email in HTML"
644 , Option "" ["id-prefix"]
646 (\arg opt -> return opt { optIdentifierPrefix = arg })
648 "" -- "Prefix to add to automatically generated HTML identifiers"
650 , Option "T" ["title-prefix"]
653 let newvars = ("title-prefix", arg) : optVariables opt
654 return opt { optVariables = newvars,
655 optStandalone = True })
657 "" -- "String to prefix to HTML window title"
662 -- add new link to end, so it is included in proper order
663 let newvars = optVariables opt ++ [("css",arg)]
664 return opt { optVariables = newvars,
665 optStandalone = True })
667 "" -- "Link to CSS style sheet"
669 , Option "" ["reference-odt"]
672 return opt { optReferenceODT = Just arg })
674 "" -- "Path of custom reference.odt"
676 , Option "" ["reference-docx"]
679 return opt { optReferenceDocx = Just arg })
681 "" -- "Path of custom reference.docx"
683 , Option "" ["epub-stylesheet"]
686 text <- UTF8.readFile arg
687 return opt { optEpubStylesheet = Just text })
689 "" -- "Path of epub.css"
691 , Option "" ["epub-cover-image"]
694 return opt { optVariables =
695 ("epub-cover-image", arg) : optVariables opt })
697 "" -- "Path of epub cover image"
699 , Option "" ["epub-metadata"]
702 text <- UTF8.readFile arg
703 return opt { optEpubMetadata = text })
705 "" -- "Path of epub metadata file"
707 , Option "" ["epub-embed-font"]
710 return opt{ optEpubFonts = arg : optEpubFonts opt })
712 "" -- "Directory of fonts to embed"
714 , Option "" ["epub-chapter-level"]
718 Just t | t >= 1 && t <= 6 ->
719 return opt { optEpubChapterLevel = t }
721 "chapter level must be a number between 1 and 6")
723 "" -- "Header level at which to split chapters in EPUB"
725 , Option "" ["latex-engine"]
728 let b = takeBaseName arg
729 if b `elem` ["pdflatex", "lualatex", "xelatex"]
730 then return opt { optLaTeXEngine = arg }
731 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
733 "" -- "Name of latex program to use in generating PDF"
735 , Option "" ["bibliography"]
737 (\arg opt -> return opt{ optMetadata = addMetadata
738 "bibliography" (readMetaValue arg)
741 ("biblio-files", dropExtension arg) :
750 return opt{ optMetadata = addMetadata "csl"
756 , Option "" ["citation-abbreviations"]
759 return opt{ optMetadata = addMetadata
760 "citation-abbreviations"
766 , Option "" ["natbib"]
768 (\opt -> return opt { optCiteMethod = Natbib }))
769 "" -- "Use natbib cite commands in LaTeX output"
771 , Option "" ["biblatex"]
773 (\opt -> return opt { optCiteMethod = Biblatex }))
774 "" -- "Use biblatex cite commands in LaTeX output"
776 , Option "m" ["latexmathml", "asciimathml"]
779 return opt { optHTMLMathMethod = LaTeXMathML arg })
781 "" -- "Use LaTeXMathML script in html output"
783 , Option "" ["mathml"]
786 return opt { optHTMLMathMethod = MathML arg })
788 "" -- "Use mathml for HTML math"
790 , Option "" ["mimetex"]
793 let url' = case arg of
795 Nothing -> "/cgi-bin/mimetex.cgi?"
796 return opt { optHTMLMathMethod = WebTeX url' })
798 "" -- "Use mimetex for HTML math"
800 , Option "" ["webtex"]
803 let url' = case arg of
805 Nothing -> "http://chart.apis.google.com/chart?cht=tx&chl="
806 return opt { optHTMLMathMethod = WebTeX url' })
808 "" -- "Use web service for HTML math"
810 , Option "" ["jsmath"]
812 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
814 "" -- "Use jsMath for HTML math"
816 , Option "" ["mathjax"]
819 let url' = case arg of
821 Nothing -> "//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
822 return opt { optHTMLMathMethod = MathJax url'})
824 "" -- "Use MathJax for HTML math"
825 , Option "" ["katex"]
830 arg <|> Just "http://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.1.0/katex.min.js"})
832 "" -- Use KaTeX for HTML Math
834 , Option "" ["katex-stylesheet"]
837 return opt { optKaTeXStylesheet = Just arg })
839 "" -- Set the KaTeX Stylesheet location
841 , Option "" ["gladtex"]
843 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
844 "" -- "Use gladtex for HTML math"
846 , Option "" ["trace"]
848 (\opt -> return opt { optTrace = True }))
849 "" -- "Turn on diagnostic tracing in readers."
851 , Option "" ["dump-args"]
853 (\opt -> return opt { optDumpArgs = True }))
854 "" -- "Print output filename and arguments to stdout."
856 , Option "" ["ignore-args"]
858 (\opt -> return opt { optIgnoreArgs = True }))
859 "" -- "Ignore command-line arguments."
861 , Option "v" ["version"]
865 defaultDatadir <- getAppUserDataDirectory "pandoc"
866 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
867 compileInfo ++ "\nDefault user data directory: " ++
868 defaultDatadir ++ copyrightMessage)
869 exitWith ExitSuccess ))
870 "" -- "Print version"
872 , Option "h" ["help"]
876 UTF8.hPutStr stdout (usageMessage prg options)
877 exitWith ExitSuccess ))
883addMetadata :: String -> MetaValue -> M.Map String MetaValue
884 -> M.Map String MetaValue
885addMetadata k v m = case M.lookup k m of
886 Nothing -> M.insert k v m
887 Just (MetaList xs) -> M.insert k
888 (MetaList (xs ++ [v])) m
889 Just x -> M.insert k (MetaList [v, x]) m
891readMetaValue :: String -> MetaValue
892readMetaValue s = case decode (UTF8.fromString s) of
893 Just (Yaml.String t) -> MetaString $ T.unpack t
894 Just (Yaml.Bool b) -> MetaBool b
897-- Returns usage message
898usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
899usageMessage programName = usageInfo
900 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
901 (wrapWords 16 78 $ readers'names) ++ "\nOutput formats: " ++
902 (wrapWords 16 78 $ writers'names) ++
903 '\n' : replicate 16 ' ' ++
904 "[*for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
906 writers'names = sort $ "pdf*" : map fst writers
907 readers'names = sort $ map fst readers
909-- Determine default reader based on source file extensions
910defaultReaderName :: String -> [FilePath] -> String
911defaultReaderName fallback [] = fallback
912defaultReaderName fallback (x:xs) =
913 case takeExtension (map toLower x) of
922 ".lhs" -> "markdown+lhs"
925 ".wiki" -> "mediawiki"
926 ".dokuwiki" -> "dokuwiki"
927 ".textile" -> "textile"
928 ".native" -> "native"
933 ".odt" -> "odt" -- so we get an "unknown reader" error
934 ".pdf" -> "pdf" -- so we get an "unknown reader" error
935 ".doc" -> "doc" -- so we get an "unknown reader" error
936 _ -> defaultReaderName fallback xs
938-- Returns True if extension of first source is .lhs
939lhsExtension :: [FilePath] -> Bool
940lhsExtension (x:_) = takeExtension x == ".lhs"
941lhsExtension _ = False
943-- Determine default writer based on output file extension
944defaultWriterName :: FilePath -> String
945defaultWriterName "-" = "html" -- no output file
947 case takeExtension (map toLower x) of
948 "" -> "markdown" -- empty extension
952 ".context" -> "context"
957 ".native" -> "native"
960 ".text" -> "markdown"
962 ".markdown" -> "markdown"
963 ".textile" -> "textile"
964 ".lhs" -> "markdown+lhs"
966 ".texinfo" -> "texinfo"
972 ".asciidoc" -> "asciidoc"
977 ['.',y] | y `elem` ['1'..'9'] -> "man"
980-- Transformations of a Pandoc document post-parsing:
982extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
983extractMedia media dir d =
984 case [fp | (fp, _, _) <- mediaDirectory media] of
987 extractMediaBag True dir media
988 return $ walk (adjustImagePath dir fps) d
990adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
991adjustImagePath dir paths (Image lab (src, tit))
992 | src `elem` paths = Image lab (dir ++ "/" ++ src, tit)
993adjustImagePath _ _ x = x
995adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
996adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
998applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
999applyTransforms transforms d = return $ foldr ($) d transforms
1001applyFilters :: [FilePath] -> [String] -> Pandoc -> IO Pandoc
1002applyFilters filters args d =
1003 foldrM ($) d $ map (flip externalFilter args) filters
1008 rawArgs <- map UTF8.decodeArg <$> getArgs
1010 let compatMode = (prg == "hsmarkdown")
1012 let (actions, args, errors) = if compatMode
1013 then ([], rawArgs, [])
1014 else getOpt Permute options rawArgs
1016 unless (null errors) $
1017 err 2 $ concat $ errors ++
1018 ["Try " ++ prg ++ " --help for more information."]
1020 let defaultOpts' = if compatMode
1021 then defaultOpts { optReader = "markdown_strict"
1022 , optWriter = "html"
1023 , optEmailObfuscation =
1024 ReferenceObfuscation }
1027 -- thread option data structure through all supplied option actions
1028 opts <- foldl (>>=) (return defaultOpts') actions
1030 let Opt { optTabStop = tabStop
1031 , optPreserveTabs = preserveTabs
1032 , optStandalone = standalone
1033 , optReader = readerName
1034 , optWriter = writerName
1035 , optParseRaw = parseRaw
1036 , optVariables = variables
1037 , optMetadata = metadata
1038 , optTableOfContents = toc
1039 , optTransforms = transforms
1040 , optTemplate = templatePath
1041 , optOutputFile = outputFile
1042 , optNumberSections = numberSections
1043 , optNumberOffset = numberFrom
1044 , optSectionDivs = sectionDivs
1045 , optIncremental = incremental
1046 , optSelfContained = selfContained
1048 , optOldDashes = oldDashes
1050 , optHtmlQTags = htmlQTags
1051 , optHighlight = highlight
1052 , optHighlightStyle = highlightStyle
1053 , optChapters = chapters
1054 , optHTMLMathMethod = mathMethod'
1055 , optReferenceODT = referenceODT
1056 , optReferenceDocx = referenceDocx
1057 , optEpubStylesheet = epubStylesheet
1058 , optEpubMetadata = epubMetadata
1059 , optEpubFonts = epubFonts
1060 , optEpubChapterLevel = epubChapterLevel
1061 , optTOCDepth = epubTOCDepth
1062 , optDumpArgs = dumpArgs
1063 , optIgnoreArgs = ignoreArgs
1064 , optReferenceLinks = referenceLinks
1065 , optWrapText = wrap
1066 , optColumns = columns
1067 , optFilters = filters
1068 , optEmailObfuscation = obfuscationMethod
1069 , optIdentifierPrefix = idPrefix
1070 , optIndentedCodeClasses = codeBlockClasses
1071 , optDataDir = mbDataDir
1072 , optCiteMethod = citeMethod
1073 , optListings = listings
1074 , optLaTeXEngine = latexEngine
1075 , optSlideLevel = slideLevel
1076 , optSetextHeaders = setextHeaders
1078 , optTeXLigatures = texLigatures
1079 , optDefaultImageExtension = defaultImageExtension
1080 , optExtractMedia = mbExtractMedia
1082 , optTrackChanges = trackChanges
1083 , optKaTeXStylesheet = katexStylesheet
1084 , optKaTeXJS = katexJS
1088 do UTF8.hPutStrLn stdout outputFile
1089 mapM_ (\arg -> UTF8.hPutStrLn stdout arg) args
1090 exitWith ExitSuccess
1092 let csscdn = "http://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.1.0/katex.min.css"
1094 case (katexJS, katexStylesheet) of
1095 (Nothing, _) -> mathMethod'
1096 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1099 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1100 let needsCiteproc = isJust (M.lookup "bibliography" metadata) &&
1101 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1102 "pandoc-citeproc" `notElem` map takeBaseName filters
1103 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1106 let sources = if ignoreArgs then [] else args
1108 datadir <- case mbDataDir of
1110 (Just <$> getAppUserDataDirectory "pandoc")
1111 (\e -> let _ = (e :: E.SomeException)
1113 Just _ -> return mbDataDir
1115 -- assign reader and writer based on options and filenames
1116 let readerName' = case map toLower readerName of
1117 [] -> defaultReaderName
1118 (if any isURI sources
1120 else "markdown") sources
1124 let writerName' = case map toLower writerName of
1125 [] -> defaultWriterName outputFile
1130 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1132 let laTeXOutput = "latex" `isPrefixOf` writerName' ||
1133 "beamer" `isPrefixOf` writerName'
1135 writer <- if ".lua" `isSuffixOf` writerName'
1136 -- note: use non-lowercased version writerName
1137 then return $ IOStringWriter $ writeCustom writerName
1138 else case getWriter writerName' of
1140 if writerName' == "pdf"
1142 "\nTo create a pdf with pandoc, use " ++
1143 "the latex or beamer writer and specify\n" ++
1144 "an output file with .pdf extension " ++
1145 "(pandoc -t latex -o filename.pdf)."
1149 reader <- if "t2t" == readerName'
1150 then (mkStringReader .
1152 (getT2TMeta sources outputFile)
1153 else case getReader readerName' of
1156 where e' = case readerName' of
1158 "\nPandoc can convert to ODT, but not from ODT.\nTry using LibreOffice to export as HTML, and convert that with pandoc."
1160 "\nPandoc can convert to PDF, but not from PDF."
1162 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1165 let standalone' = standalone || not (isTextFormat writerName') || pdfOutput
1167 templ <- case templatePath of
1168 _ | not standalone' -> return ""
1170 deftemp <- getDefaultTemplate datadir writerName'
1175 -- strip off extensions
1176 let format = takeWhile (`notElem` "+-") writerName'
1177 let tp' = case takeExtension tp of
1180 E.catch (UTF8.readFile tp')
1181 (\e -> if isDoesNotExistError e
1183 (readDataFileUTF8 datadir
1184 ("templates" </> tp'))
1185 (\e' -> let _ = (e' :: E.SomeException)
1189 variables' <- case mathMethod of
1190 LaTeXMathML Nothing -> do
1191 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1192 return $ ("mathml-script", s) : variables
1193 MathML Nothing -> do
1194 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1195 return $ ("mathml-script", s) : variables
1196 _ -> return variables
1198 variables'' <- if "dzslides" `isPrefixOf` writerName'
1200 dztempl <- readDataFileUTF8 datadir
1201 ("dzslides" </> "template.html")
1202 let dzline = "<!-- {{{{ dzslides core"
1203 let dzcore = unlines
1204 $ dropWhile (not . (dzline `isPrefixOf`))
1206 return $ ("dzslides-core", dzcore) : variables'
1207 else return variables'
1209 let sourceURL = case sources of
1211 (x:_) -> case parseURI x of
1213 | uriScheme u `elem` ["http:","https:"] ->
1214 Just $ show u{ uriQuery = "",
1218 let readerOpts = def{ readerSmart = smart || (texLigatures &&
1219 (laTeXOutput || "context" `isPrefixOf` writerName'))
1220 , readerStandalone = standalone'
1221 , readerParseRaw = parseRaw
1222 , readerColumns = columns
1223 , readerTabStop = tabStop
1224 , readerOldDashes = oldDashes
1225 , readerIndentedCodeClasses = codeBlockClasses
1226 , readerApplyMacros = not laTeXOutput
1227 , readerDefaultImageExtension = defaultImageExtension
1228 , readerTrace = trace
1229 , readerTrackChanges = trackChanges
1232 when (not (isTextFormat writerName') && outputFile == "-") $
1233 err 5 $ "Cannot write " ++ writerName' ++ " output to stdout.\n" ++
1234 "Specify an output file using the -o option."
1236 let readSources [] = mapM readSource ["-"]
1237 readSources srcs = mapM readSource srcs
1238 readSource "-" = UTF8.getContents
1239 readSource src = case parseURI src of
1240 Just u | uriScheme u `elem` ["http:","https:"] ->
1242 _ -> UTF8.readFile src
1247 Right (bs,_) -> return $ UTF8.toString bs
1249 let readFiles [] = error "Cannot read archive from stdin"
1250 readFiles (x:_) = B.readFile x
1252 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1256 let handleIncludes' = if readerName' == "latex" ||
1257 readerName' == "latex+lhs"
1263 StringReader r-> (, mempty) <$>
1265 handleIncludes' . convertTabs . intercalate "\n" >=>
1266 r readerOpts ) sources
1267 ByteStringReader r -> readFiles sources >>= r readerOpts
1269 let writerOptions = def { writerStandalone = standalone',
1270 writerTemplate = templ,
1271 writerVariables = variables'',
1272 writerTabStop = tabStop,
1273 writerTableOfContents = toc,
1274 writerHTMLMathMethod = mathMethod,
1275 writerIncremental = incremental,
1276 writerCiteMethod = citeMethod,
1277 writerIgnoreNotes = False,
1278 writerNumberSections = numberSections,
1279 writerNumberOffset = numberFrom,
1280 writerSectionDivs = sectionDivs,
1281 writerReferenceLinks = referenceLinks,
1282 writerWrapText = wrap,
1283 writerColumns = columns,
1284 writerEmailObfuscation = obfuscationMethod,
1285 writerIdentifierPrefix = idPrefix,
1286 writerSourceURL = sourceURL,
1287 writerUserDataDir = datadir,
1288 writerHtml5 = html5,
1289 writerHtmlQTags = htmlQTags,
1290 writerChapters = chapters,
1291 writerListings = listings,
1292 writerBeamer = False,
1293 writerSlideLevel = slideLevel,
1294 writerHighlight = highlight,
1295 writerHighlightStyle = highlightStyle,
1296 writerSetextHeaders = setextHeaders,
1297 writerTeXLigatures = texLigatures,
1298 writerEpubMetadata = epubMetadata,
1299 writerEpubStylesheet = epubStylesheet,
1300 writerEpubFonts = epubFonts,
1301 writerEpubChapterLevel = epubChapterLevel,
1302 writerTOCDepth = epubTOCDepth,
1303 writerReferenceODT = referenceODT,
1304 writerReferenceDocx = referenceDocx,
1305 writerMediaBag = media
1309 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1310 adjustMetadata metadata >=>
1311 applyTransforms transforms >=>
1312 applyFilters filters' [writerName']) doc
1314 let writeBinary :: B.ByteString -> IO ()
1315 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1317 let writerFn :: FilePath -> String -> IO ()
1318 writerFn "-" = UTF8.putStr
1319 writerFn f = UTF8.writeFile f
1322 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1323 IOByteStringWriter f -> f writerOptions doc' >>= writeBinary
1326 -- make sure writer is latex or beamer
1327 unless laTeXOutput $
1328 err 47 $ "cannot produce pdf output with " ++ writerName' ++
1331 -- check for latex program
1332 mbLatex <- findExecutable latexEngine
1333 when (mbLatex == Nothing) $
1334 err 41 $ latexEngine ++ " not found. " ++
1335 latexEngine ++ " is needed for pdf output."
1337 res <- makePDF latexEngine f writerOptions doc'
1339 Right pdf -> writeBinary pdf
1341 B.hPutStr stderr $ err'
1342 B.hPut stderr $ B.pack [10]
1343 err 43 "Error producing PDF from TeX source"
1344 | otherwise -> selfcontain (f writerOptions doc' ++
1345 ['\n' | not standalone'])
1346 >>= writerFn outputFile . handleEntities
1347 where htmlFormat = writerName' `elem`
1348 ["html","html+lhs","html5","html5+lhs",
1349 "s5","slidy","slideous","dzslides","revealjs"]
1350 selfcontain = if selfContained && htmlFormat
1351 then makeSelfContained writerOptions
1353 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