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