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