git.iko.soy/typeable/pandoc
on sable help sign in
typeable/pandoc/pandoc.hs
1171 lines 51 kB history raw
1{-# LANGUAGE CPP #-}
2{-
3Copyright (C) 2006-2013 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
18-}
20{- |
21 Module : Main
22 Copyright : Copyright (C) 2006-2013 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
26 Stability : alpha
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
30writers.
31-}
32module Main where
33import Text.Pandoc
34import Text.Pandoc.Builder (setMeta)
35import Text.Pandoc.PDF (makePDF)
36import Text.Pandoc.Readers.LaTeX (handleIncludes)
37import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
38 safeRead, headerShift, normalize, err, warn )
39import Text.Pandoc.XML ( toEntities )
40import Text.Pandoc.SelfContained ( makeSelfContained )
41import Text.Pandoc.Process (pipeProcess)
42import Text.Highlighting.Kate ( languages, Style, tango, pygments,
43 espresso, zenburn, kate, haddock, monochrome )
44import System.Environment ( getArgs, getProgName )
45import System.Exit ( exitWith, ExitCode (..) )
46import System.FilePath
47import System.Console.GetOpt
48import Data.Char ( toLower )
49import Data.List ( intercalate, isPrefixOf, sort )
50import System.Directory ( getAppUserDataDirectory, findExecutable )
51import System.IO ( stdout, stderr )
52import System.IO.Error ( isDoesNotExistError )
53import qualified Control.Exception as E
54import Control.Exception.Extensible ( throwIO )
55import qualified Text.Pandoc.UTF8 as UTF8
56import Control.Monad (when, unless, liftM)
57import Data.Foldable (foldrM)
58import Network.HTTP (simpleHTTP, mkRequest, getResponseBody, RequestMethod(..))
59import Network.URI (parseURI, isURI, URI(..))
60import qualified Data.ByteString.Lazy as B
61import qualified Data.ByteString as BS
62import Data.Aeson (eitherDecode', encode)
63import qualified Data.Map as M
64import System.IO.Error(ioeGetErrorType)
65import GHC.IO.Exception (IOErrorType(ResourceVanished))
66import Data.Yaml (decode)
67import qualified Data.Yaml as Yaml
68import qualified Data.Text as T
70copyrightMessage :: String
71copyrightMessage = "\nCopyright (C) 2006-2013 John MacFarlane\n" ++
72 "Web: http://johnmacfarlane.net/pandoc\n" ++
73 "This is free software; see the source for copying conditions. There is no\n" ++
74 "warranty, not even for merchantability or fitness for a particular purpose."
76compileInfo :: String
77compileInfo =
78 "\nCompiled with texmath " ++
79 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
80 ".\nSyntax highlighting is supported for the following languages:\n " ++
81 wrapWords 4 78
82 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
84-- | Converts a list of strings into a single string with the items printed as
85-- comma separated words in lines with a maximum line length.
86wrapWords :: Int -> Int -> [String] -> String
87wrapWords indent c = wrap' (c - indent) (c - indent)
88 where wrap' _ _ [] = ""
89 wrap' cols remaining (x:xs) = if remaining == cols
90 then x ++ wrap' cols (remaining - length x) xs
91 else if (length x + 1) > remaining
92 then ",\n" ++ replicate indent ' ' ++ x ++ wrap' cols (cols - length x) xs
93 else ", " ++ x ++ wrap' cols (remaining - (length x + 2)) xs
95isTextFormat :: String -> Bool
96isTextFormat s = takeWhile (`notElem` "+-") s `notElem` ["odt","docx","epub","epub3"]
98externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
99externalFilter f args' d = do
100 (exitcode, outbs, errbs) <- E.handle filterException $
101 pipeProcess Nothing f args' $ encode d
102 when (not $ B.null errbs) $ B.hPutStr stderr errbs
103 case exitcode of
104 ExitSuccess -> return $ either error id $ eitherDecode' outbs
105 ExitFailure _ -> err 83 $ "Error running filter " ++ f
106 where filterException :: E.SomeException -> IO a
107 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
108 if ioeGetErrorType `fmap` E.fromException e ==
109 Just ResourceVanished
110 then f ++ " not found in path"
111 else show e
113-- | Data structure for command line options.
114data Opt = Opt
115 { optTabStop :: Int -- ^ Number of spaces per tab
116 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
117 , optStandalone :: Bool -- ^ Include header, footer
118 , optReader :: String -- ^ Reader format
119 , optWriter :: String -- ^ Writer format
120 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
121 , optTableOfContents :: Bool -- ^ Include table of contents
122 , optTransforms :: [Pandoc -> Pandoc] -- ^ Doc transforms to apply
123 , optTemplate :: Maybe FilePath -- ^ Custom template
124 , optVariables :: [(String,String)] -- ^ Template variables to set
125 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
126 , optOutputFile :: String -- ^ Name of output file
127 , optNumberSections :: Bool -- ^ Number sections in LaTeX
128 , optNumberOffset :: [Int] -- ^ Starting number for sections
129 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
130 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
131 , optSelfContained :: Bool -- ^ Make HTML accessible offline
132 , optSmart :: Bool -- ^ Use smart typography
133 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
134 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
135 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
136 , optHighlight :: Bool -- ^ Highlight source code
137 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
138 , optChapters :: Bool -- ^ Use chapter for top-level sects
139 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
140 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
141 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
142 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
143 , optEpubMetadata :: String -- ^ EPUB metadata
144 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
145 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
146 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
147 , optDumpArgs :: Bool -- ^ Output command-line arguments
148 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
149 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
150 , optWrapText :: Bool -- ^ Wrap text
151 , optColumns :: Int -- ^ Line length in characters
152 , optFilters :: [FilePath] -- ^ Filters to apply
153 , optEmailObfuscation :: ObfuscationMethod
154 , optIdentifierPrefix :: String
155 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
156 , optDataDir :: Maybe FilePath
157 , optCiteMethod :: CiteMethod -- ^ Method to output cites
158 , optListings :: Bool -- ^ Use listings package for code blocks
159 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
160 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
161 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
162 , optAscii :: Bool -- ^ Use ascii characters only in html
163 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
164 , optDefaultImageExtension :: String -- ^ Default image extension
165 }
167-- | Defaults for command-line options.
168defaultOpts :: Opt
169defaultOpts = Opt
170 { optTabStop = 4
171 , optPreserveTabs = False
172 , optStandalone = False
173 , optReader = "" -- null for default reader
174 , optWriter = "" -- null for default writer
175 , optParseRaw = False
176 , optTableOfContents = False
177 , optTransforms = []
178 , optTemplate = Nothing
179 , optVariables = []
180 , optMetadata = M.empty
181 , optOutputFile = "-" -- "-" means stdout
182 , optNumberSections = False
183 , optNumberOffset = [0,0,0,0,0,0]
184 , optSectionDivs = False
185 , optIncremental = False
186 , optSelfContained = False
187 , optSmart = False
188 , optOldDashes = False
189 , optHtml5 = False
190 , optHtmlQTags = False
191 , optHighlight = True
192 , optHighlightStyle = pygments
193 , optChapters = False
194 , optHTMLMathMethod = PlainMath
195 , optReferenceODT = Nothing
196 , optReferenceDocx = Nothing
197 , optEpubStylesheet = Nothing
198 , optEpubMetadata = ""
199 , optEpubFonts = []
200 , optEpubChapterLevel = 1
201 , optTOCDepth = 3
202 , optDumpArgs = False
203 , optIgnoreArgs = False
204 , optReferenceLinks = False
205 , optWrapText = True
206 , optColumns = 72
207 , optFilters = []
208 , optEmailObfuscation = JavascriptObfuscation
209 , optIdentifierPrefix = ""
210 , optIndentedCodeClasses = []
211 , optDataDir = Nothing
212 , optCiteMethod = Citeproc
213 , optListings = False
214 , optLaTeXEngine = "pdflatex"
215 , optSlideLevel = Nothing
216 , optSetextHeaders = True
217 , optAscii = False
218 , optTeXLigatures = True
219 , optDefaultImageExtension = ""
220 }
222-- | A list of functions, each transforming the options data structure
223-- in response to a command-line option.
224options :: [OptDescr (Opt -> IO Opt)]
225options =
226 [ Option "fr" ["from","read"]
227 (ReqArg
228 (\arg opt -> return opt { optReader = map toLower arg })
229 "FORMAT")
230 ""
232 , Option "tw" ["to","write"]
233 (ReqArg
234 (\arg opt -> return opt { optWriter = map toLower arg })
235 "FORMAT")
236 ""
238 , Option "o" ["output"]
239 (ReqArg
240 (\arg opt -> return opt { optOutputFile = arg })
241 "FILENAME")
242 "" -- "Name of output file"
244 , Option "" ["data-dir"]
245 (ReqArg
246 (\arg opt -> return opt { optDataDir = Just arg })
247 "DIRECTORY") -- "Directory containing pandoc data files."
248 ""
250 , Option "" ["strict"]
251 (NoArg
252 (\opt -> do
253 err 59 $ "The --strict option has been removed.\n" ++
254 "Use `markdown_strict' input or output format instead."
255 return opt ))
256 "" -- "Disable markdown syntax extensions"
258 , Option "R" ["parse-raw"]
259 (NoArg
260 (\opt -> return opt { optParseRaw = True }))
261 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
263 , Option "S" ["smart"]
264 (NoArg
265 (\opt -> return opt { optSmart = True }))
266 "" -- "Use smart quotes, dashes, and ellipses"
268 , Option "" ["old-dashes"]
269 (NoArg
270 (\opt -> return opt { optSmart = True
271 , optOldDashes = True }))
272 "" -- "Use smart quotes, dashes, and ellipses"
274 , Option "" ["base-header-level"]
275 (ReqArg
276 (\arg opt ->
277 case safeRead arg of
278 Just t | t > 0 -> do
279 let oldTransforms = optTransforms opt
280 let shift = t - 1
281 return opt{ optTransforms =
282 headerShift shift : oldTransforms }
283 _ -> err 19
284 "base-header-level must be a number > 0")
285 "NUMBER")
286 "" -- "Headers base level"
288 , Option "" ["indented-code-classes"]
289 (ReqArg
290 (\arg opt -> return opt { optIndentedCodeClasses = words $
291 map (\c -> if c == ',' then ' ' else c) arg })
292 "STRING")
293 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
295 , Option "F" ["filter"]
296 (ReqArg
297 (\arg opt -> return opt { optFilters = arg : optFilters opt })
298 "PROGRAM")
299 "" -- "External JSON filter"
301 , Option "" ["normalize"]
302 (NoArg
303 (\opt -> return opt { optTransforms =
304 normalize : optTransforms opt } ))
305 "" -- "Normalize the Pandoc AST"
307 , Option "p" ["preserve-tabs"]
308 (NoArg
309 (\opt -> return opt { optPreserveTabs = True }))
310 "" -- "Preserve tabs instead of converting to spaces"
312 , Option "" ["tab-stop"]
313 (ReqArg
314 (\arg opt ->
315 case safeRead arg of
316 Just t | t > 0 -> return opt { optTabStop = t }
317 _ -> err 31
318 "tab-stop must be a number greater than 0")
319 "NUMBER")
320 "" -- "Tab stop (default 4)"
322 , Option "s" ["standalone"]
323 (NoArg
324 (\opt -> return opt { optStandalone = True }))
325 "" -- "Include needed header and footer on output"
327 , Option "" ["template"]
328 (ReqArg
329 (\arg opt -> do
330 return opt{ optTemplate = Just arg,
331 optStandalone = True })
332 "FILENAME")
333 "" -- "Use custom template"
335 , Option "M" ["metadata"]
336 (ReqArg
337 (\arg opt -> do
338 let (key,val) = case break (`elem` ":=") arg of
339 (k,_:v) -> (k, readMetaValue v)
340 (k,_) -> (k, MetaBool True)
341 return opt{ optMetadata = addMetadata key val
342 $ optMetadata opt })
343 "KEY[:VALUE]")
344 ""
346 , Option "V" ["variable"]
347 (ReqArg
348 (\arg opt -> do
349 let (key,val) = case break (`elem` ":=") arg of
350 (k,_:v) -> (k,v)
351 (k,_) -> (k,"true")
352 return opt{ optVariables = (key,val) : optVariables opt })
353 "KEY[:VALUE]")
354 ""
356 , Option "D" ["print-default-template"]
357 (ReqArg
358 (\arg _ -> do
359 templ <- getDefaultTemplate Nothing arg
360 case templ of
361 Right t -> UTF8.hPutStr stdout t
362 Left e -> error $ show e
363 exitWith ExitSuccess)
364 "FORMAT")
365 "" -- "Print default template for FORMAT"
367 , Option "" ["print-default-data-file"]
368 (ReqArg
369 (\arg _ -> do
370 readDataFile Nothing arg >>= BS.hPutStr stdout
371 exitWith ExitSuccess)
372 "FILE")
373 "" -- "Print default data file"
375 , Option "" ["no-wrap"]
376 (NoArg
377 (\opt -> return opt { optWrapText = False }))
378 "" -- "Do not wrap text in output"
380 , Option "" ["columns"]
381 (ReqArg
382 (\arg opt ->
383 case safeRead arg of
384 Just t | t > 0 -> return opt { optColumns = t }
385 _ -> err 33 $
386 "columns must be a number greater than 0")
387 "NUMBER")
388 "" -- "Length of line in characters"
390 , Option "" ["toc", "table-of-contents"]
391 (NoArg
392 (\opt -> return opt { optTableOfContents = True }))
393 "" -- "Include table of contents"
395 , Option "" ["toc-depth"]
396 (ReqArg
397 (\arg opt -> do
398 case safeRead arg of
399 Just t | t >= 1 && t <= 6 ->
400 return opt { optTOCDepth = t }
401 _ -> err 57 $
402 "TOC level must be a number between 1 and 6")
403 "NUMBER")
404 "" -- "Number of levels to include in TOC"
406 , Option "" ["no-highlight"]
407 (NoArg
408 (\opt -> return opt { optHighlight = False }))
409 "" -- "Don't highlight source code"
411 , Option "" ["highlight-style"]
412 (ReqArg
413 (\arg opt -> do
414 newStyle <- case map toLower arg of
415 "pygments" -> return pygments
416 "tango" -> return tango
417 "espresso" -> return espresso
418 "zenburn" -> return zenburn
419 "kate" -> return kate
420 "monochrome" -> return monochrome
421 "haddock" -> return haddock
422 _ -> err 39 $
423 "Unknown style :" ++ arg
424 return opt{ optHighlightStyle = newStyle })
425 "STYLE")
426 "" -- "Style for highlighted code"
428 , Option "H" ["include-in-header"]
429 (ReqArg
430 (\arg opt -> do
431 text <- UTF8.readFile arg
432 -- add new ones to end, so they're included in order specified
433 let newvars = optVariables opt ++ [("header-includes",text)]
434 return opt { optVariables = newvars,
435 optStandalone = True })
436 "FILENAME")
437 "" -- "File to include at end of header (implies -s)"
439 , Option "B" ["include-before-body"]
440 (ReqArg
441 (\arg opt -> do
442 text <- UTF8.readFile arg
443 -- add new ones to end, so they're included in order specified
444 let newvars = optVariables opt ++ [("include-before",text)]
445 return opt { optVariables = newvars,
446 optStandalone = True })
447 "FILENAME")
448 "" -- "File to include before document body"
450 , Option "A" ["include-after-body"]
451 (ReqArg
452 (\arg opt -> do
453 text <- UTF8.readFile arg
454 -- add new ones to end, so they're included in order specified
455 let newvars = optVariables opt ++ [("include-after",text)]
456 return opt { optVariables = newvars,
457 optStandalone = True })
458 "FILENAME")
459 "" -- "File to include after document body"
461 , Option "" ["self-contained"]
462 (NoArg
463 (\opt -> return opt { optSelfContained = True,
464 optVariables = ("slidy-url","slidy") :
465 optVariables opt,
466 optStandalone = True }))
467 "" -- "Make slide shows include all the needed js and css"
469 , Option "" ["offline"]
470 (NoArg
471 (\opt -> do warn $ "--offline is deprecated. Use --self-contained instead."
472 return opt { optSelfContained = True,
473 optStandalone = True }))
474 "" -- "Make slide shows include all the needed js and css"
475 -- deprecated synonym for --self-contained
477 , Option "5" ["html5"]
478 (NoArg
479 (\opt -> do
480 warn $ "--html5 is deprecated. "
481 ++ "Use the html5 output format instead."
482 return opt { optHtml5 = True }))
483 "" -- "Produce HTML5 in HTML output"
485 , Option "" ["html-q-tags"]
486 (NoArg
487 (\opt -> do
488 return opt { optHtmlQTags = True }))
489 "" -- "Use <q> tags for quotes in HTML"
491 , Option "" ["ascii"]
492 (NoArg
493 (\opt -> return opt { optAscii = True }))
494 "" -- "Use ascii characters only in HTML output"
496 , Option "" ["reference-links"]
497 (NoArg
498 (\opt -> return opt { optReferenceLinks = True } ))
499 "" -- "Use reference links in parsing HTML"
501 , Option "" ["atx-headers"]
502 (NoArg
503 (\opt -> return opt { optSetextHeaders = False } ))
504 "" -- "Use atx-style headers for markdown"
506 , Option "" ["chapters"]
507 (NoArg
508 (\opt -> return opt { optChapters = True }))
509 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
511 , Option "N" ["number-sections"]
512 (NoArg
513 (\opt -> return opt { optNumberSections = True }))
514 "" -- "Number sections in LaTeX"
516 , Option "" ["number-offset"]
517 (ReqArg
518 (\arg opt ->
519 case safeRead ('[':arg ++ "]") of
520 Just ns -> return opt { optNumberOffset = ns,
521 optNumberSections = True }
522 _ -> err 57 "could not parse number-offset")
523 "NUMBERS")
524 "" -- "Starting number for sections, subsections, etc."
526 , Option "" ["no-tex-ligatures"]
527 (NoArg
528 (\opt -> return opt { optTeXLigatures = False }))
529 "" -- "Don't use tex ligatures for quotes, dashes"
531 , Option "" ["listings"]
532 (NoArg
533 (\opt -> return opt { optListings = True }))
534 "" -- "Use listings package for LaTeX code blocks"
536 , Option "i" ["incremental"]
537 (NoArg
538 (\opt -> return opt { optIncremental = True }))
539 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
541 , Option "" ["slide-level"]
542 (ReqArg
543 (\arg opt -> do
544 case safeRead arg of
545 Just t | t >= 1 && t <= 6 ->
546 return opt { optSlideLevel = Just t }
547 _ -> err 39 $
548 "slide level must be a number between 1 and 6")
549 "NUMBER")
550 "" -- "Force header level for slides"
552 , Option "" ["section-divs"]
553 (NoArg
554 (\opt -> return opt { optSectionDivs = True }))
555 "" -- "Put sections in div tags in HTML"
557 , Option "" ["default-image-extension"]
558 (ReqArg
559 (\arg opt -> return opt { optDefaultImageExtension = arg })
560 "extension")
561 "" -- "Default extension for extensionless images"
563 , Option "" ["email-obfuscation"]
564 (ReqArg
565 (\arg opt -> do
566 method <- case arg of
567 "references" -> return ReferenceObfuscation
568 "javascript" -> return JavascriptObfuscation
569 "none" -> return NoObfuscation
570 _ -> err 6
571 ("Unknown obfuscation method: " ++ arg)
572 return opt { optEmailObfuscation = method })
573 "none|javascript|references")
574 "" -- "Method for obfuscating email in HTML"
576 , Option "" ["id-prefix"]
577 (ReqArg
578 (\arg opt -> return opt { optIdentifierPrefix = arg })
579 "STRING")
580 "" -- "Prefix to add to automatically generated HTML identifiers"
582 , Option "T" ["title-prefix"]
583 (ReqArg
584 (\arg opt -> do
585 let newvars = ("title-prefix", arg) : optVariables opt
586 return opt { optVariables = newvars,
587 optStandalone = True })
588 "STRING")
589 "" -- "String to prefix to HTML window title"
591 , Option "c" ["css"]
592 (ReqArg
593 (\arg opt -> do
594 -- add new link to end, so it is included in proper order
595 let newvars = optVariables opt ++ [("css",arg)]
596 return opt { optVariables = newvars,
597 optStandalone = True })
598 "URL")
599 "" -- "Link to CSS style sheet"
601 , Option "" ["reference-odt"]
602 (ReqArg
603 (\arg opt -> do
604 return opt { optReferenceODT = Just arg })
605 "FILENAME")
606 "" -- "Path of custom reference.odt"
608 , Option "" ["reference-docx"]
609 (ReqArg
610 (\arg opt -> do
611 return opt { optReferenceDocx = Just arg })
612 "FILENAME")
613 "" -- "Path of custom reference.docx"
615 , Option "" ["epub-stylesheet"]
616 (ReqArg
617 (\arg opt -> do
618 text <- UTF8.readFile arg
619 return opt { optEpubStylesheet = Just text })
620 "FILENAME")
621 "" -- "Path of epub.css"
623 , Option "" ["epub-cover-image"]
624 (ReqArg
625 (\arg opt ->
626 return opt { optVariables =
627 ("epub-cover-image", arg) : optVariables opt })
628 "FILENAME")
629 "" -- "Path of epub cover image"
631 , Option "" ["epub-metadata"]
632 (ReqArg
633 (\arg opt -> do
634 text <- UTF8.readFile arg
635 return opt { optEpubMetadata = text })
636 "FILENAME")
637 "" -- "Path of epub metadata file"
639 , Option "" ["epub-embed-font"]
640 (ReqArg
641 (\arg opt -> do
642 return opt{ optEpubFonts = arg : optEpubFonts opt })
643 "FILE")
644 "" -- "Directory of fonts to embed"
646 , Option "" ["epub-chapter-level"]
647 (ReqArg
648 (\arg opt -> do
649 case safeRead arg of
650 Just t | t >= 1 && t <= 6 ->
651 return opt { optEpubChapterLevel = t }
652 _ -> err 59 $
653 "chapter level must be a number between 1 and 6")
654 "NUMBER")
655 "" -- "Header level at which to split chapters in EPUB"
657 , Option "" ["latex-engine"]
658 (ReqArg
659 (\arg opt -> do
660 let b = takeBaseName arg
661 if (b == "pdflatex" || b == "lualatex" || b == "xelatex")
662 then return opt { optLaTeXEngine = arg }
663 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
664 "PROGRAM")
665 "" -- "Name of latex program to use in generating PDF"
667 , Option "" ["bibliography"]
668 (ReqArg
669 (\arg opt -> return opt{ optMetadata = addMetadata
670 "bibliography" (readMetaValue arg)
671 $ optMetadata opt
672 })
673 "FILE")
674 ""
676 , Option "" ["csl"]
677 (ReqArg
678 (\arg opt ->
679 return opt{ optMetadata = addMetadata "csl"
680 (readMetaValue arg)
681 $ optMetadata opt })
682 "FILE")
683 ""
685 , Option "" ["citation-abbreviations"]
686 (ReqArg
687 (\arg opt ->
688 return opt{ optMetadata = addMetadata
689 "citation-abbreviations"
690 (readMetaValue arg)
691 $ optMetadata opt })
692 "FILE")
693 ""
695 , Option "" ["natbib"]
696 (NoArg
697 (\opt -> return opt { optCiteMethod = Natbib }))
698 "" -- "Use natbib cite commands in LaTeX output"
700 , Option "" ["biblatex"]
701 (NoArg
702 (\opt -> return opt { optCiteMethod = Biblatex }))
703 "" -- "Use biblatex cite commands in LaTeX output"
705 , Option "m" ["latexmathml", "asciimathml"]
706 (OptArg
707 (\arg opt ->
708 return opt { optHTMLMathMethod = LaTeXMathML arg })
709 "URL")
710 "" -- "Use LaTeXMathML script in html output"
712 , Option "" ["mathml"]
713 (OptArg
714 (\arg opt ->
715 return opt { optHTMLMathMethod = MathML arg })
716 "URL")
717 "" -- "Use mathml for HTML math"
719 , Option "" ["mimetex"]
720 (OptArg
721 (\arg opt -> do
722 let url' = case arg of
723 Just u -> u ++ "?"
724 Nothing -> "/cgi-bin/mimetex.cgi?"
725 return opt { optHTMLMathMethod = WebTeX url' })
726 "URL")
727 "" -- "Use mimetex for HTML math"
729 , Option "" ["webtex"]
730 (OptArg
731 (\arg opt -> do
732 let url' = case arg of
733 Just u -> u
734 Nothing -> "http://chart.apis.google.com/chart?cht=tx&chl="
735 return opt { optHTMLMathMethod = WebTeX url' })
736 "URL")
737 "" -- "Use web service for HTML math"
739 , Option "" ["jsmath"]
740 (OptArg
741 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
742 "URL")
743 "" -- "Use jsMath for HTML math"
745 , Option "" ["mathjax"]
746 (OptArg
747 (\arg opt -> do
748 let url' = case arg of
749 Just u -> u
750 Nothing -> "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
751 return opt { optHTMLMathMethod = MathJax url'})
752 "URL")
753 "" -- "Use MathJax for HTML math"
755 , Option "" ["gladtex"]
756 (NoArg
757 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
758 "" -- "Use gladtex for HTML math"
760 , Option "" ["dump-args"]
761 (NoArg
762 (\opt -> return opt { optDumpArgs = True }))
763 "" -- "Print output filename and arguments to stdout."
765 , Option "" ["ignore-args"]
766 (NoArg
767 (\opt -> return opt { optIgnoreArgs = True }))
768 "" -- "Ignore command-line arguments."
770 , Option "v" ["version"]
771 (NoArg
772 (\_ -> do
773 prg <- getProgName
774 defaultDatadir <- getAppUserDataDirectory "pandoc"
775 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
776 compileInfo ++ "\nDefault user data directory: " ++
777 defaultDatadir ++ copyrightMessage)
778 exitWith ExitSuccess ))
779 "" -- "Print version"
781 , Option "h" ["help"]
782 (NoArg
783 (\_ -> do
784 prg <- getProgName
785 UTF8.hPutStr stdout (usageMessage prg options)
786 exitWith ExitSuccess ))
787 "" -- "Show help"
789 ]
791addMetadata :: String -> MetaValue -> M.Map String MetaValue
792 -> M.Map String MetaValue
793addMetadata k v m = case M.lookup k m of
794 Nothing -> M.insert k v m
795 Just (MetaList xs) -> M.insert k
796 (MetaList (xs ++ [v])) m
797 Just x -> M.insert k (MetaList [v, x]) m
799readMetaValue :: String -> MetaValue
800readMetaValue s = case decode (UTF8.fromString s) of
801 Just (Yaml.String t) -> MetaString $ T.unpack t
802 Just (Yaml.Bool b) -> MetaBool b
803 _ -> MetaString s
805-- Returns usage message
806usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
807usageMessage programName = usageInfo
808 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
809 (wrapWords 16 78 $ readers'names) ++ "\nOutput formats: " ++
810 (wrapWords 16 78 $ writers'names) ++
811 '\n' : replicate 16 ' ' ++
812 "[*for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
813 where
814 writers'names = sort $ "pdf*" : map fst writers
815 readers'names = sort $ map fst readers
817-- Determine default reader based on source file extensions
818defaultReaderName :: String -> [FilePath] -> String
819defaultReaderName fallback [] = fallback
820defaultReaderName fallback (x:xs) =
821 case takeExtension (map toLower x) of
822 ".xhtml" -> "html"
823 ".html" -> "html"
824 ".htm" -> "html"
825 ".tex" -> "latex"
826 ".latex" -> "latex"
827 ".ltx" -> "latex"
828 ".rst" -> "rst"
829 ".lhs" -> "markdown+lhs"
830 ".db" -> "docbook"
831 ".opml" -> "opml"
832 ".wiki" -> "mediawiki"
833 ".textile" -> "textile"
834 ".native" -> "native"
835 ".json" -> "json"
836 _ -> defaultReaderName fallback xs
838-- Returns True if extension of first source is .lhs
839lhsExtension :: [FilePath] -> Bool
840lhsExtension (x:_) = takeExtension x == ".lhs"
841lhsExtension _ = False
843-- Determine default writer based on output file extension
844defaultWriterName :: FilePath -> String
845defaultWriterName "-" = "html" -- no output file
846defaultWriterName x =
847 case takeExtension (map toLower x) of
848 "" -> "markdown" -- empty extension
849 ".tex" -> "latex"
850 ".latex" -> "latex"
851 ".ltx" -> "latex"
852 ".context" -> "context"
853 ".ctx" -> "context"
854 ".rtf" -> "rtf"
855 ".rst" -> "rst"
856 ".s5" -> "s5"
857 ".native" -> "native"
858 ".json" -> "json"
859 ".txt" -> "markdown"
860 ".text" -> "markdown"
861 ".md" -> "markdown"
862 ".markdown" -> "markdown"
863 ".textile" -> "textile"
864 ".lhs" -> "markdown+lhs"
865 ".texi" -> "texinfo"
866 ".texinfo" -> "texinfo"
867 ".db" -> "docbook"
868 ".odt" -> "odt"
869 ".docx" -> "docx"
870 ".epub" -> "epub"
871 ".org" -> "org"
872 ".asciidoc" -> "asciidoc"
873 ".pdf" -> "latex"
874 ".fb2" -> "fb2"
875 ".sable" -> "sable"
876 ".opml" -> "opml"
877 ['.',y] | y `elem` ['1'..'9'] -> "man"
878 _ -> "html"
880main :: IO ()
881main = do
883 rawArgs <- liftM (map UTF8.decodeArg) getArgs
884 prg <- getProgName
885 let compatMode = (prg == "hsmarkdown")
887 let (actions, args, errors) = if compatMode
888 then ([], rawArgs, [])
889 else getOpt Permute options rawArgs
891 unless (null errors) $
892 err 2 $ concat $ errors ++
893 ["Try " ++ prg ++ " --help for more information."]
895 let defaultOpts' = if compatMode
896 then defaultOpts { optReader = "markdown_strict"
897 , optWriter = "html"
898 , optEmailObfuscation =
899 ReferenceObfuscation }
900 else defaultOpts
902 -- thread option data structure through all supplied option actions
903 opts <- foldl (>>=) (return defaultOpts') actions
905 let Opt { optTabStop = tabStop
906 , optPreserveTabs = preserveTabs
907 , optStandalone = standalone
908 , optReader = readerName
909 , optWriter = writerName
910 , optParseRaw = parseRaw
911 , optVariables = variables
912 , optMetadata = metadata
913 , optTableOfContents = toc
914 , optTransforms = transforms
915 , optTemplate = templatePath
916 , optOutputFile = outputFile
917 , optNumberSections = numberSections
918 , optNumberOffset = numberFrom
919 , optSectionDivs = sectionDivs
920 , optIncremental = incremental
921 , optSelfContained = selfContained
922 , optSmart = smart
923 , optOldDashes = oldDashes
924 , optHtml5 = html5
925 , optHtmlQTags = htmlQTags
926 , optHighlight = highlight
927 , optHighlightStyle = highlightStyle
928 , optChapters = chapters
929 , optHTMLMathMethod = mathMethod
930 , optReferenceODT = referenceODT
931 , optReferenceDocx = referenceDocx
932 , optEpubStylesheet = epubStylesheet
933 , optEpubMetadata = epubMetadata
934 , optEpubFonts = epubFonts
935 , optEpubChapterLevel = epubChapterLevel
936 , optTOCDepth = epubTOCDepth
937 , optDumpArgs = dumpArgs
938 , optIgnoreArgs = ignoreArgs
939 , optReferenceLinks = referenceLinks
940 , optWrapText = wrap
941 , optColumns = columns
942 , optFilters = filters
943 , optEmailObfuscation = obfuscationMethod
944 , optIdentifierPrefix = idPrefix
945 , optIndentedCodeClasses = codeBlockClasses
946 , optDataDir = mbDataDir
947 , optCiteMethod = citeMethod
948 , optListings = listings
949 , optLaTeXEngine = latexEngine
950 , optSlideLevel = slideLevel
951 , optSetextHeaders = setextHeaders
952 , optAscii = ascii
953 , optTeXLigatures = texLigatures
954 , optDefaultImageExtension = defaultImageExtension
955 } = opts
957 when dumpArgs $
958 do UTF8.hPutStrLn stdout outputFile
959 mapM_ (\arg -> UTF8.hPutStrLn stdout arg) args
960 exitWith ExitSuccess
962 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
963 let filters' = case M.lookup "bibliography" metadata of
964 Just _ | all (\f -> takeBaseName f /= "pandoc-citeproc")
965 filters -> "pandoc-citeproc" : filters
966 _ -> filters
967 let plugins = map externalFilter filters'
969 let sources = if ignoreArgs then [] else args
971 datadir <- case mbDataDir of
972 Nothing -> E.catch
973 (liftM Just $ getAppUserDataDirectory "pandoc")
974 (\e -> let _ = (e :: E.SomeException)
975 in return Nothing)
976 Just _ -> return mbDataDir
978 -- assign reader and writer based on options and filenames
979 let readerName' = if null readerName
980 then let fallback = if any isURI sources
981 then "html"
982 else "markdown"
983 in defaultReaderName fallback sources
984 else readerName
986 let writerName' = if null writerName
987 then defaultWriterName outputFile
988 else writerName
990 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
992 let laTeXOutput = "latex" `isPrefixOf` writerName' ||
993 "beamer" `isPrefixOf` writerName'
995 when pdfOutput $ do
996 -- make sure writer is latex or beamer
997 unless laTeXOutput $
998 err 47 $ "cannot produce pdf output with " ++ writerName' ++ " writer"
999 -- check for latex program
1000 mbLatex <- findExecutable latexEngine
1001 case mbLatex of
1002 Nothing -> err 41 $
1003 latexEngine ++ " not found. " ++
1004 latexEngine ++ " is needed for pdf output."
1005 Just _ -> return ()
1007 reader <- case getReader readerName' of
1008 Right r -> return r
1009 Left e -> err 7 e
1011 let standalone' = standalone || not (isTextFormat writerName') || pdfOutput
1013 templ <- case templatePath of
1014 _ | not standalone' -> return ""
1015 Nothing -> do
1016 deftemp <- getDefaultTemplate datadir writerName'
1017 case deftemp of
1018 Left e -> throwIO e
1019 Right t -> return t
1020 Just tp -> do
1021 -- strip off extensions
1022 let format = takeWhile (`notElem` "+-") writerName'
1023 let tp' = case takeExtension tp of
1024 "" -> tp <.> format
1025 _ -> tp
1026 E.catch (UTF8.readFile tp')
1027 (\e -> if isDoesNotExistError e
1028 then E.catch
1029 (readDataFileUTF8 datadir
1030 ("templates" </> tp'))
1031 (\e' -> let _ = (e' :: E.SomeException)
1032 in throwIO e')
1033 else throwIO e)
1035 variables' <- case mathMethod of
1036 LaTeXMathML Nothing -> do
1037 s <- readDataFileUTF8 datadir
1038 ("LaTeXMathML.js")
1039 return $ ("mathml-script", s) : variables
1040 MathML Nothing -> do
1041 s <- readDataFileUTF8 datadir
1042 ("MathMLinHTML.js")
1043 return $ ("mathml-script", s) : variables
1044 _ -> return variables
1046 variables'' <- if "dzslides" `isPrefixOf` writerName'
1047 then do
1048 dztempl <- readDataFileUTF8 datadir
1049 ("dzslides" </> "template.html")
1050 let dzcore = unlines $ dropWhile (not . isPrefixOf "<!-- {{{{ dzslides core")
1051 $ lines dztempl
1052 return $ ("dzslides-core", dzcore) : variables'
1053 else return variables'
1054 let sourceURL = case sources of
1055 [] -> Nothing
1056 (x:_) -> case parseURI x of
1057 Just u
1058 | uriScheme u `elem` ["http:","https:"] ->
1059 Just $ show u{ uriPath = "",
1060 uriQuery = "",
1061 uriFragment = "" }
1062 _ -> Nothing
1064 let readerOpts = def{ readerSmart = smart || (texLigatures &&
1065 (laTeXOutput || "context" `isPrefixOf` writerName'))
1066 , readerStandalone = standalone'
1067 , readerParseRaw = parseRaw
1068 , readerColumns = columns
1069 , readerTabStop = tabStop
1070 , readerOldDashes = oldDashes
1071 , readerIndentedCodeClasses = codeBlockClasses
1072 , readerApplyMacros = not laTeXOutput
1073 , readerDefaultImageExtension = defaultImageExtension
1076 let writerOptions = def { writerStandalone = standalone',
1077 writerTemplate = templ,
1078 writerVariables = variables'',
1079 writerTabStop = tabStop,
1080 writerTableOfContents = toc,
1081 writerHTMLMathMethod = mathMethod,
1082 writerIncremental = incremental,
1083 writerCiteMethod = citeMethod,
1084 writerIgnoreNotes = False,
1085 writerNumberSections = numberSections,
1086 writerNumberOffset = numberFrom,
1087 writerSectionDivs = sectionDivs,
1088 writerReferenceLinks = referenceLinks,
1089 writerWrapText = wrap,
1090 writerColumns = columns,
1091 writerEmailObfuscation = obfuscationMethod,
1092 writerIdentifierPrefix = idPrefix,
1093 writerSourceURL = sourceURL,
1094 writerUserDataDir = datadir,
1095 writerHtml5 = html5,
1096 writerHtmlQTags = htmlQTags,
1097 writerChapters = chapters,
1098 writerListings = listings,
1099 writerBeamer = False,
1100 writerSlideLevel = slideLevel,
1101 writerHighlight = highlight,
1102 writerHighlightStyle = highlightStyle,
1103 writerSetextHeaders = setextHeaders,
1104 writerTeXLigatures = texLigatures,
1105 writerEpubMetadata = epubMetadata,
1106 writerEpubStylesheet = epubStylesheet,
1107 writerEpubFonts = epubFonts,
1108 writerEpubChapterLevel = epubChapterLevel,
1109 writerTOCDepth = epubTOCDepth,
1110 writerReferenceODT = referenceODT,
1111 writerReferenceDocx = referenceDocx
1114 when (not (isTextFormat writerName') && outputFile == "-") $
1115 err 5 $ "Cannot write " ++ writerName' ++ " output to stdout.\n" ++
1116 "Specify an output file using the -o option."
1118 let readSources [] = mapM readSource ["-"]
1119 readSources srcs = mapM readSource srcs
1120 readSource "-" = UTF8.getContents
1121 readSource src = case parseURI src of
1122 Just u | uriScheme u `elem` ["http:","https:"] ->
1123 readURI u
1124 _ -> UTF8.readFile src
1125 readURI uri = simpleHTTP (mkRequest GET uri) >>= getResponseBody >>=
1126 return . UTF8.toStringLazy -- treat all as UTF8
1128 let convertTabs = tabFilter (if preserveTabs then 0 else tabStop)
1130 let handleIncludes' = if readerName' == "latex" || readerName' == "latex+lhs"
1131 then handleIncludes
1132 else return
1134 doc <- readSources sources >>=
1135 handleIncludes' . convertTabs . intercalate "\n" >>=
1136 reader readerOpts
1139 let doc0 = M.foldWithKey setMeta doc metadata
1140 let doc1 = foldr ($) doc0 transforms
1141 doc2 <- foldrM ($) doc1 $ map ($ [writerName']) plugins
1143 let writeBinary :: B.ByteString -> IO ()
1144 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1146 let writerFn :: FilePath -> String -> IO ()
1147 writerFn "-" = UTF8.putStr
1148 writerFn f = UTF8.writeFile f
1150 case getWriter writerName' of
1151 Left e -> err 9 e
1152 Right (IOStringWriter f) -> f writerOptions doc2 >>= writerFn outputFile
1153 Right (IOByteStringWriter f) -> f writerOptions doc2 >>= writeBinary
1154 Right (PureStringWriter f)
1155 | pdfOutput -> do
1156 res <- makePDF latexEngine f writerOptions doc2
1157 case res of
1158 Right pdf -> writeBinary pdf
1159 Left err' -> err 43 $ UTF8.toStringLazy err'
1160 | otherwise -> selfcontain (f writerOptions doc2 ++
1161 ['\n' | not standalone'])
1162 >>= writerFn outputFile . handleEntities
1163 where htmlFormat = writerName' `elem`
1164 ["html","html+lhs","html5","html5+lhs",
1165 "s5","slidy","slideous","dzslides","revealjs"]
1166 selfcontain = if selfContained && htmlFormat
1167 then makeSelfContained datadir
1168 else return
1169 handleEntities = if htmlFormat && ascii
1170 then toEntities
1171 else id