git.iko.soy/typeable/pandoc
on 4eff607 help sign in
typeable/pandoc/pandoc.hs
1459 lines 64 kB history raw
1{-# LANGUAGE CPP, TupleSections, ScopedTypeVariables, PatternGuards #-}
2{-
3Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18-}
20{- |
21 Module : Main
22 Copyright : Copyright (C) 2006-2016 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.Walk (walk)
37import Text.Pandoc.Readers.LaTeX (handleIncludes)
38import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
39 safeRead, headerShift, normalize, err, warn,
40 openURL )
41import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag )
42import Text.Pandoc.XML ( toEntities )
43import Text.Pandoc.SelfContained ( makeSelfContained )
44import Text.Pandoc.Process (pipeProcess)
45import Text.Highlighting.Kate ( languages, Style, tango, pygments,
46 espresso, zenburn, kate, haddock, monochrome )
47import System.Environment ( getArgs, getProgName )
48import System.Exit ( ExitCode (..), exitSuccess )
49import System.FilePath
50import System.Console.GetOpt
51import Data.Char ( toLower, toUpper )
52import Data.List ( delete, intercalate, isPrefixOf, isSuffixOf, sort )
53import System.Directory ( getAppUserDataDirectory, findExecutable,
54 doesFileExist, Permissions(..), getPermissions )
55import System.IO ( stdout, stderr )
56import System.IO.Error ( isDoesNotExistError )
57import qualified Control.Exception as E
58import Control.Exception.Extensible ( throwIO )
59import qualified Text.Pandoc.UTF8 as UTF8
60import Control.Monad (when, unless, (>=>))
61import Data.Maybe (fromMaybe, isNothing, isJust)
62import Data.Foldable (foldrM)
63import Network.URI (parseURI, isURI, URI(..))
64import qualified Data.ByteString.Lazy as B
65import qualified Data.ByteString as BS
66import Data.Aeson (eitherDecode', encode)
67import qualified Data.Map as M
68import Data.Yaml (decode)
69import qualified Data.Yaml as Yaml
70import qualified Data.Text as T
71import Control.Applicative ((<|>))
72import Text.Pandoc.Readers.Txt2Tags (getT2TMeta)
73import Paths_pandoc (getDataDir)
74import Text.Printf (printf)
75import Text.Pandoc.Error
77type Transform = Pandoc -> Pandoc
79copyrightMessage :: String
80copyrightMessage = intercalate "\n" [
81 "",
82 "Copyright (C) 2006-2016 John MacFarlane",
83 "Web: http://pandoc.org",
84 "This is free software; see the source for copying conditions.",
85 "There is no warranty, not even for merchantability or fitness",
86 "for a particular purpose." ]
88compileInfo :: String
89compileInfo =
90 "\nCompiled with texmath " ++
91 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
92 ".\nSyntax highlighting is supported for the following languages:\n " ++
93 wrapWords 4 78
94 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
96-- | Converts a list of strings into a single string with the items printed as
97-- comma separated words in lines with a maximum line length.
98wrapWords :: Int -> Int -> [String] -> String
99wrapWords indent c = wrap' (c - indent) (c - indent)
100 where
101 wrap' _ _ [] = ""
102 wrap' cols remaining (x:xs)
103 | remaining == cols =
104 x ++ wrap' cols (remaining - length x) xs
105 | (length x + 1) > remaining =
106 ",\n" ++ replicate indent ' ' ++ x ++
107 wrap' cols (cols - length x) xs
108 | otherwise =
109 ", " ++ x ++
110 wrap' cols (remaining - length x - 2) xs
112isTextFormat :: String -> Bool
113isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
115externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
116externalFilter f args' d = do
117 mbexe <- if '/' `elem` f
118 -- don't check PATH if filter name has a path
119 then return Nothing
120 -- we catch isDoesNotExistError because this will
121 -- be triggered if PATH not set:
122 else E.catch (findExecutable f)
123 (\e -> if isDoesNotExistError e
124 then return Nothing
125 else throwIO e)
126 (f', args'') <- case mbexe of
127 Just x -> return (x, args')
128 Nothing -> do
129 exists <- doesFileExist f
130 if exists
131 then do
132 isExecutable <- executable `fmap`
133 getPermissions f
134 return $
135 case map toLower $ takeExtension f of
136 _ | isExecutable -> (f, args')
137 ".py" -> ("python", f:args')
138 ".hs" -> ("runhaskell", f:args')
139 ".pl" -> ("perl", f:args')
140 ".rb" -> ("ruby", f:args')
141 ".php" -> ("php", f:args')
142 ".js" -> ("node", f:args')
143 _ -> (f, args')
144 else err 85 $ "Filter " ++ f ++ " not found"
145 when (f' /= f) $ do
146 mbExe <- findExecutable f'
147 when (isNothing mbExe) $
148 err 83 $ "Error running filter " ++ f ++ "\n" ++
149 show f' ++ " not found in path."
150 (exitcode, outbs, errbs) <- E.handle filterException $
151 pipeProcess Nothing f' args'' $ encode d
152 unless (B.null errbs) $ B.hPutStr stderr errbs
153 case exitcode of
154 ExitSuccess -> return $ either error id $ eitherDecode' outbs
155 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
156 "Filter returned error status " ++ show ec
157 where filterException :: E.SomeException -> IO a
158 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
159 show e
161-- | Data structure for command line options.
162data Opt = Opt
163 { optTabStop :: Int -- ^ Number of spaces per tab
164 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
165 , optStandalone :: Bool -- ^ Include header, footer
166 , optReader :: String -- ^ Reader format
167 , optWriter :: String -- ^ Writer format
168 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
169 , optTableOfContents :: Bool -- ^ Include table of contents
170 , optTransforms :: [Transform] -- ^ Doc transforms to apply
171 , optTemplate :: Maybe FilePath -- ^ Custom template
172 , optVariables :: [(String,String)] -- ^ Template variables to set
173 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
174 , optOutputFile :: String -- ^ Name of output file
175 , optNumberSections :: Bool -- ^ Number sections in LaTeX
176 , optNumberOffset :: [Int] -- ^ Starting number for sections
177 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
178 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
179 , optSelfContained :: Bool -- ^ Make HTML accessible offline
180 , optSmart :: Bool -- ^ Use smart typography
181 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
182 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
183 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
184 , optHighlight :: Bool -- ^ Highlight source code
185 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
186 , optChapters :: Bool -- ^ Use chapter for top-level sects
187 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
188 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
189 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
190 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
191 , optEpubMetadata :: String -- ^ EPUB metadata
192 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
193 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
194 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
195 , optDumpArgs :: Bool -- ^ Output command-line arguments
196 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
197 , optVerbose :: Bool -- ^ Verbose diagnostic output
198 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
199 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
200 , optDpi :: Int -- ^ Dpi
201 , optWrapText :: WrapOption -- ^ Options for wrapping text
202 , optColumns :: Int -- ^ Line length in characters
203 , optFilters :: [FilePath] -- ^ Filters to apply
204 , optEmailObfuscation :: ObfuscationMethod
205 , optIdentifierPrefix :: String
206 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
207 , optDataDir :: Maybe FilePath
208 , optCiteMethod :: CiteMethod -- ^ Method to output cites
209 , optListings :: Bool -- ^ Use listings package for code blocks
210 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
211 , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine
212 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
213 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
214 , optAscii :: Bool -- ^ Use ascii characters only in html
215 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
216 , optDefaultImageExtension :: String -- ^ Default image extension
217 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
218 , optTrace :: Bool -- ^ Print debug information
219 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
220 , optFileScope :: Bool -- ^ Parse input files before combining
221 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
222 , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX
223 }
225-- | Defaults for command-line options.
226defaultOpts :: Opt
227defaultOpts = Opt
228 { optTabStop = 4
229 , optPreserveTabs = False
230 , optStandalone = False
231 , optReader = "" -- null for default reader
232 , optWriter = "" -- null for default writer
233 , optParseRaw = False
234 , optTableOfContents = False
235 , optTransforms = []
236 , optTemplate = Nothing
237 , optVariables = []
238 , optMetadata = M.empty
239 , optOutputFile = "-" -- "-" means stdout
240 , optNumberSections = False
241 , optNumberOffset = [0,0,0,0,0,0]
242 , optSectionDivs = False
243 , optIncremental = False
244 , optSelfContained = False
245 , optSmart = False
246 , optOldDashes = False
247 , optHtml5 = False
248 , optHtmlQTags = False
249 , optHighlight = True
250 , optHighlightStyle = pygments
251 , optChapters = False
252 , optHTMLMathMethod = PlainMath
253 , optReferenceODT = Nothing
254 , optReferenceDocx = Nothing
255 , optEpubStylesheet = Nothing
256 , optEpubMetadata = ""
257 , optEpubFonts = []
258 , optEpubChapterLevel = 1
259 , optTOCDepth = 3
260 , optDumpArgs = False
261 , optIgnoreArgs = False
262 , optVerbose = False
263 , optReferenceLinks = False
264 , optReferenceLocation = EndOfDocument
265 , optDpi = 96
266 , optWrapText = WrapAuto
267 , optColumns = 72
268 , optFilters = []
269 , optEmailObfuscation = NoObfuscation
270 , optIdentifierPrefix = ""
271 , optIndentedCodeClasses = []
272 , optDataDir = Nothing
273 , optCiteMethod = Citeproc
274 , optListings = False
275 , optLaTeXEngine = "pdflatex"
276 , optLaTeXEngineArgs = []
277 , optSlideLevel = Nothing
278 , optSetextHeaders = True
279 , optAscii = False
280 , optTeXLigatures = True
281 , optDefaultImageExtension = ""
282 , optExtractMedia = Nothing
283 , optTrace = False
284 , optTrackChanges = AcceptChanges
285 , optFileScope = False
286 , optKaTeXStylesheet = Nothing
287 , optKaTeXJS = Nothing
288 }
290-- | A list of functions, each transforming the options data structure
291-- in response to a command-line option.
292options :: [OptDescr (Opt -> IO Opt)]
293options =
294 [ Option "fr" ["from","read"]
295 (ReqArg
296 (\arg opt -> return opt { optReader = arg })
297 "FORMAT")
298 ""
300 , Option "tw" ["to","write"]
301 (ReqArg
302 (\arg opt -> return opt { optWriter = arg })
303 "FORMAT")
304 ""
306 , Option "o" ["output"]
307 (ReqArg
308 (\arg opt -> return opt { optOutputFile = arg })
309 "FILENAME")
310 "" -- "Name of output file"
312 , Option "" ["data-dir"]
313 (ReqArg
314 (\arg opt -> return opt { optDataDir = Just arg })
315 "DIRECTORY") -- "Directory containing pandoc data files."
316 ""
318 , Option "R" ["parse-raw"]
319 (NoArg
320 (\opt -> return opt { optParseRaw = True }))
321 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
323 , Option "S" ["smart"]
324 (NoArg
325 (\opt -> return opt { optSmart = True }))
326 "" -- "Use smart quotes, dashes, and ellipses"
328 , Option "" ["old-dashes"]
329 (NoArg
330 (\opt -> return opt { optSmart = True
331 , optOldDashes = True }))
332 "" -- "Use smart quotes, dashes, and ellipses"
334 , Option "" ["base-header-level"]
335 (ReqArg
336 (\arg opt ->
337 case safeRead arg of
338 Just t | t > 0 -> do
339 let oldTransforms = optTransforms opt
340 let shift = t - 1
341 return opt{ optTransforms =
342 headerShift shift : oldTransforms }
343 _ -> err 19
344 "base-header-level must be a number > 0")
345 "NUMBER")
346 "" -- "Headers base level"
348 , Option "" ["indented-code-classes"]
349 (ReqArg
350 (\arg opt -> return opt { optIndentedCodeClasses = words $
351 map (\c -> if c == ',' then ' ' else c) arg })
352 "STRING")
353 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
355 , Option "F" ["filter"]
356 (ReqArg
357 (\arg opt -> return opt { optFilters = arg : optFilters opt })
358 "PROGRAM")
359 "" -- "External JSON filter"
361 , Option "" ["normalize"]
362 (NoArg
363 (\opt -> return opt { optTransforms =
364 normalize : optTransforms opt } ))
365 "" -- "Normalize the Pandoc AST"
367 , Option "p" ["preserve-tabs"]
368 (NoArg
369 (\opt -> return opt { optPreserveTabs = True }))
370 "" -- "Preserve tabs instead of converting to spaces"
372 , Option "" ["tab-stop"]
373 (ReqArg
374 (\arg opt ->
375 case safeRead arg of
376 Just t | t > 0 -> return opt { optTabStop = t }
377 _ -> err 31
378 "tab-stop must be a number greater than 0")
379 "NUMBER")
380 "" -- "Tab stop (default 4)"
382 , Option "" ["track-changes"]
383 (ReqArg
384 (\arg opt -> do
385 action <- case arg of
386 "accept" -> return AcceptChanges
387 "reject" -> return RejectChanges
388 "all" -> return AllChanges
389 _ -> err 6
390 ("Unknown option for track-changes: " ++ arg)
391 return opt { optTrackChanges = action })
392 "accept|reject|all")
393 "" -- "Accepting or reject MS Word track-changes.""
395 , Option "" ["file-scope"]
396 (NoArg
397 (\opt -> return opt { optFileScope = True }))
398 "" -- "Parse input files before combining"
400 , Option "" ["extract-media"]
401 (ReqArg
402 (\arg opt ->
403 return opt { optExtractMedia = Just arg })
404 "PATH")
405 "" -- "Directory to which to extract embedded media"
407 , Option "s" ["standalone"]
408 (NoArg
409 (\opt -> return opt { optStandalone = True }))
410 "" -- "Include needed header and footer on output"
412 , Option "" ["template"]
413 (ReqArg
414 (\arg opt ->
415 return opt{ optTemplate = Just arg,
416 optStandalone = True })
417 "FILENAME")
418 "" -- "Use custom template"
420 , Option "M" ["metadata"]
421 (ReqArg
422 (\arg opt -> do
423 let (key,val) = case break (`elem` ":=") arg of
424 (k,_:v) -> (k, readMetaValue v)
425 (k,_) -> (k, MetaBool True)
426 return opt{ optMetadata = addMetadata key val
427 $ optMetadata opt })
428 "KEY[:VALUE]")
429 ""
431 , Option "V" ["variable"]
432 (ReqArg
433 (\arg opt -> do
434 let (key,val) = case break (`elem` ":=") arg of
435 (k,_:v) -> (k,v)
436 (k,_) -> (k,"true")
437 return opt{ optVariables = (key,val) : optVariables opt })
438 "KEY[:VALUE]")
439 ""
441 , Option "D" ["print-default-template"]
442 (ReqArg
443 (\arg _ -> do
444 templ <- getDefaultTemplate Nothing arg
445 case templ of
446 Right t -> UTF8.hPutStr stdout t
447 Left e -> error $ show e
448 exitSuccess)
449 "FORMAT")
450 "" -- "Print default template for FORMAT"
452 , Option "" ["print-default-data-file"]
453 (ReqArg
454 (\arg _ -> do
455 readDataFile Nothing arg >>= BS.hPutStr stdout
456 exitSuccess)
457 "FILE")
458 "" -- "Print default data file"
460 , Option "" ["dpi"]
461 (ReqArg
462 (\arg opt ->
463 case safeRead arg of
464 Just t | t > 0 -> return opt { optDpi = t }
465 _ -> err 31
466 "dpi must be a number greater than 0")
467 "NUMBER")
468 "" -- "Dpi (default 96)"
470 , Option "" ["no-wrap"]
471 (NoArg
472 (\opt -> do warn $ "--no-wrap is deprecated. " ++
473 "Use --wrap=none or --wrap=preserve instead."
474 return opt { optWrapText = WrapNone }))
475 ""
477 , Option "" ["wrap"]
478 (ReqArg
479 (\arg opt ->
480 case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
481 Just o -> return opt { optWrapText = o }
482 Nothing -> err 77 "--wrap must be auto, none, or preserve")
483 "[auto|none|preserve]")
484 "" -- "Option for wrapping text in output"
486 , Option "" ["columns"]
487 (ReqArg
488 (\arg opt ->
489 case safeRead arg of
490 Just t | t > 0 -> return opt { optColumns = t }
491 _ -> err 33
492 "columns must be a number greater than 0")
493 "NUMBER")
494 "" -- "Length of line in characters"
496 , Option "" ["toc", "table-of-contents"]
497 (NoArg
498 (\opt -> return opt { optTableOfContents = True }))
499 "" -- "Include table of contents"
501 , Option "" ["toc-depth"]
502 (ReqArg
503 (\arg opt ->
504 case safeRead arg of
505 Just t | t >= 1 && t <= 6 ->
506 return opt { optTOCDepth = t }
507 _ -> err 57
508 "TOC level must be a number between 1 and 6")
509 "NUMBER")
510 "" -- "Number of levels to include in TOC"
512 , Option "" ["no-highlight"]
513 (NoArg
514 (\opt -> return opt { optHighlight = False }))
515 "" -- "Don't highlight source code"
517 , Option "" ["highlight-style"]
518 (ReqArg
519 (\arg opt -> do
520 newStyle <- case map toLower arg of
521 "pygments" -> return pygments
522 "tango" -> return tango
523 "espresso" -> return espresso
524 "zenburn" -> return zenburn
525 "kate" -> return kate
526 "monochrome" -> return monochrome
527 "haddock" -> return haddock
528 _ -> err 39 $
529 "Unknown style :" ++ arg
530 return opt{ optHighlightStyle = newStyle })
531 "STYLE")
532 "" -- "Style for highlighted code"
534 , Option "H" ["include-in-header"]
535 (ReqArg
536 (\arg opt -> do
537 text <- UTF8.readFile arg
538 -- add new ones to end, so they're included in order specified
539 let newvars = optVariables opt ++ [("header-includes",text)]
540 return opt { optVariables = newvars,
541 optStandalone = True })
542 "FILENAME")
543 "" -- "File to include at end of header (implies -s)"
545 , Option "B" ["include-before-body"]
546 (ReqArg
547 (\arg opt -> do
548 text <- UTF8.readFile arg
549 -- add new ones to end, so they're included in order specified
550 let newvars = optVariables opt ++ [("include-before",text)]
551 return opt { optVariables = newvars,
552 optStandalone = True })
553 "FILENAME")
554 "" -- "File to include before document body"
556 , Option "A" ["include-after-body"]
557 (ReqArg
558 (\arg opt -> do
559 text <- UTF8.readFile arg
560 -- add new ones to end, so they're included in order specified
561 let newvars = optVariables opt ++ [("include-after",text)]
562 return opt { optVariables = newvars,
563 optStandalone = True })
564 "FILENAME")
565 "" -- "File to include after document body"
567 , Option "" ["self-contained"]
568 (NoArg
569 (\opt -> return opt { optSelfContained = True,
570 optStandalone = True }))
571 "" -- "Make slide shows include all the needed js and css"
573 , Option "" ["html-q-tags"]
574 (NoArg
575 (\opt ->
576 return opt { optHtmlQTags = True }))
577 "" -- "Use <q> tags for quotes in HTML"
579 , Option "" ["ascii"]
580 (NoArg
581 (\opt -> return opt { optAscii = True }))
582 "" -- "Use ascii characters only in HTML output"
584 , Option "" ["reference-links"]
585 (NoArg
586 (\opt -> return opt { optReferenceLinks = True } ))
587 "" -- "Use reference links in parsing HTML"
589 , Option "" ["reference-location"]
590 (ReqArg
591 (\arg opt -> do
592 action <- case arg of
593 "block" -> return EndOfBlock
594 "section" -> return EndOfSection
595 "document" -> return EndOfDocument
596 _ -> err 6
597 ("Unknown option for reference-location: " ++ arg)
598 return opt { optReferenceLocation = action })
599 "block|section|document")
600 "" -- "Accepting or reject MS Word track-changes.""
602 , Option "" ["atx-headers"]
603 (NoArg
604 (\opt -> return opt { optSetextHeaders = False } ))
605 "" -- "Use atx-style headers for markdown"
607 , Option "" ["chapters"]
608 (NoArg
609 (\opt -> return opt { optChapters = True }))
610 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
612 , Option "N" ["number-sections"]
613 (NoArg
614 (\opt -> return opt { optNumberSections = True }))
615 "" -- "Number sections in LaTeX"
617 , Option "" ["number-offset"]
618 (ReqArg
619 (\arg opt ->
620 case safeRead ('[':arg ++ "]") of
621 Just ns -> return opt { optNumberOffset = ns,
622 optNumberSections = True }
623 _ -> err 57 "could not parse number-offset")
624 "NUMBERS")
625 "" -- "Starting number for sections, subsections, etc."
627 , Option "" ["no-tex-ligatures"]
628 (NoArg
629 (\opt -> return opt { optTeXLigatures = False }))
630 "" -- "Don't use tex ligatures for quotes, dashes"
632 , Option "" ["listings"]
633 (NoArg
634 (\opt -> return opt { optListings = True }))
635 "" -- "Use listings package for LaTeX code blocks"
637 , Option "i" ["incremental"]
638 (NoArg
639 (\opt -> return opt { optIncremental = True }))
640 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
642 , Option "" ["slide-level"]
643 (ReqArg
644 (\arg opt ->
645 case safeRead arg of
646 Just t | t >= 1 && t <= 6 ->
647 return opt { optSlideLevel = Just t }
648 _ -> err 39
649 "slide level must be a number between 1 and 6")
650 "NUMBER")
651 "" -- "Force header level for slides"
653 , Option "" ["section-divs"]
654 (NoArg
655 (\opt -> return opt { optSectionDivs = True }))
656 "" -- "Put sections in div tags in HTML"
658 , Option "" ["default-image-extension"]
659 (ReqArg
660 (\arg opt -> return opt { optDefaultImageExtension = arg })
661 "extension")
662 "" -- "Default extension for extensionless images"
664 , Option "" ["email-obfuscation"]
665 (ReqArg
666 (\arg opt -> do
667 method <- case arg of
668 "references" -> return ReferenceObfuscation
669 "javascript" -> return JavascriptObfuscation
670 "none" -> return NoObfuscation
671 _ -> err 6
672 ("Unknown obfuscation method: " ++ arg)
673 return opt { optEmailObfuscation = method })
674 "none|javascript|references")
675 "" -- "Method for obfuscating email in HTML"
677 , Option "" ["id-prefix"]
678 (ReqArg
679 (\arg opt -> return opt { optIdentifierPrefix = arg })
680 "STRING")
681 "" -- "Prefix to add to automatically generated HTML identifiers"
683 , Option "T" ["title-prefix"]
684 (ReqArg
685 (\arg opt -> do
686 let newvars = ("title-prefix", arg) : optVariables opt
687 return opt { optVariables = newvars,
688 optStandalone = True })
689 "STRING")
690 "" -- "String to prefix to HTML window title"
692 , Option "c" ["css"]
693 (ReqArg
694 (\arg opt -> do
695 -- add new link to end, so it is included in proper order
696 let newvars = optVariables opt ++ [("css",arg)]
697 return opt { optVariables = newvars,
698 optStandalone = True })
699 "URL")
700 "" -- "Link to CSS style sheet"
702 , Option "" ["reference-odt"]
703 (ReqArg
704 (\arg opt ->
705 return opt { optReferenceODT = Just arg })
706 "FILENAME")
707 "" -- "Path of custom reference.odt"
709 , Option "" ["reference-docx"]
710 (ReqArg
711 (\arg opt ->
712 return opt { optReferenceDocx = Just arg })
713 "FILENAME")
714 "" -- "Path of custom reference.docx"
716 , Option "" ["epub-stylesheet"]
717 (ReqArg
718 (\arg opt -> do
719 text <- UTF8.readFile arg
720 return opt { optEpubStylesheet = Just text })
721 "FILENAME")
722 "" -- "Path of epub.css"
724 , Option "" ["epub-cover-image"]
725 (ReqArg
726 (\arg opt ->
727 return opt { optVariables =
728 ("epub-cover-image", arg) : optVariables opt })
729 "FILENAME")
730 "" -- "Path of epub cover image"
732 , Option "" ["epub-metadata"]
733 (ReqArg
734 (\arg opt -> do
735 text <- UTF8.readFile arg
736 return opt { optEpubMetadata = text })
737 "FILENAME")
738 "" -- "Path of epub metadata file"
740 , Option "" ["epub-embed-font"]
741 (ReqArg
742 (\arg opt ->
743 return opt{ optEpubFonts = arg : optEpubFonts opt })
744 "FILE")
745 "" -- "Directory of fonts to embed"
747 , Option "" ["epub-chapter-level"]
748 (ReqArg
749 (\arg opt ->
750 case safeRead arg of
751 Just t | t >= 1 && t <= 6 ->
752 return opt { optEpubChapterLevel = t }
753 _ -> err 59
754 "chapter level must be a number between 1 and 6")
755 "NUMBER")
756 "" -- "Header level at which to split chapters in EPUB"
758 , Option "" ["latex-engine"]
759 (ReqArg
760 (\arg opt -> do
761 let b = takeBaseName arg
762 if b `elem` ["pdflatex", "lualatex", "xelatex"]
763 then return opt { optLaTeXEngine = arg }
764 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
765 "PROGRAM")
766 "" -- "Name of latex program to use in generating PDF"
768 , Option "" ["latex-engine-opt"]
769 (ReqArg
770 (\arg opt -> do
771 let oldArgs = optLaTeXEngineArgs opt
772 return opt { optLaTeXEngineArgs = arg : oldArgs })
773 "STRING")
774 "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used"
776 , Option "" ["bibliography"]
777 (ReqArg
778 (\arg opt -> return opt{ optMetadata = addMetadata
779 "bibliography" (readMetaValue arg)
780 $ optMetadata opt
781 })
782 "FILE")
783 ""
785 , Option "" ["csl"]
786 (ReqArg
787 (\arg opt ->
788 return opt{ optMetadata = addMetadata "csl"
789 (readMetaValue arg)
790 $ optMetadata opt })
791 "FILE")
792 ""
794 , Option "" ["citation-abbreviations"]
795 (ReqArg
796 (\arg opt ->
797 return opt{ optMetadata = addMetadata
798 "citation-abbreviations"
799 (readMetaValue arg)
800 $ optMetadata opt })
801 "FILE")
802 ""
804 , Option "" ["natbib"]
805 (NoArg
806 (\opt -> return opt { optCiteMethod = Natbib }))
807 "" -- "Use natbib cite commands in LaTeX output"
809 , Option "" ["biblatex"]
810 (NoArg
811 (\opt -> return opt { optCiteMethod = Biblatex }))
812 "" -- "Use biblatex cite commands in LaTeX output"
814 , Option "m" ["latexmathml", "asciimathml"]
815 (OptArg
816 (\arg opt ->
817 return opt { optHTMLMathMethod = LaTeXMathML arg })
818 "URL")
819 "" -- "Use LaTeXMathML script in html output"
821 , Option "" ["mathml"]
822 (OptArg
823 (\arg opt ->
824 return opt { optHTMLMathMethod = MathML arg })
825 "URL")
826 "" -- "Use mathml for HTML math"
828 , Option "" ["mimetex"]
829 (OptArg
830 (\arg opt -> do
831 let url' = case arg of
832 Just u -> u ++ "?"
833 Nothing -> "/cgi-bin/mimetex.cgi?"
834 return opt { optHTMLMathMethod = WebTeX url' })
835 "URL")
836 "" -- "Use mimetex for HTML math"
838 , Option "" ["webtex"]
839 (OptArg
840 (\arg opt -> do
841 let url' = fromMaybe "https://latex.codecogs.com/png.latex?" arg
842 return opt { optHTMLMathMethod = WebTeX url' })
843 "URL")
844 "" -- "Use web service for HTML math"
846 , Option "" ["jsmath"]
847 (OptArg
848 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
849 "URL")
850 "" -- "Use jsMath for HTML math"
852 , Option "" ["mathjax"]
853 (OptArg
854 (\arg opt -> do
855 let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg
856 return opt { optHTMLMathMethod = MathJax url'})
857 "URL")
858 "" -- "Use MathJax for HTML math"
859 , Option "" ["katex"]
860 (OptArg
861 (\arg opt ->
862 return opt
863 { optKaTeXJS =
864 arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
865 "URL")
866 "" -- Use KaTeX for HTML Math
868 , Option "" ["katex-stylesheet"]
869 (ReqArg
870 (\arg opt ->
871 return opt { optKaTeXStylesheet = Just arg })
872 "URL")
873 "" -- Set the KaTeX Stylesheet location
875 , Option "" ["gladtex"]
876 (NoArg
877 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
878 "" -- "Use gladtex for HTML math"
880 , Option "" ["trace"]
881 (NoArg
882 (\opt -> return opt { optTrace = True }))
883 "" -- "Turn on diagnostic tracing in readers."
885 , Option "" ["dump-args"]
886 (NoArg
887 (\opt -> return opt { optDumpArgs = True }))
888 "" -- "Print output filename and arguments to stdout."
890 , Option "" ["ignore-args"]
891 (NoArg
892 (\opt -> return opt { optIgnoreArgs = True }))
893 "" -- "Ignore command-line arguments."
895 , Option "" ["verbose"]
896 (NoArg
897 (\opt -> return opt { optVerbose = True }))
898 "" -- "Verbose diagnostic output."
900 , Option "" ["bash-completion"]
901 (NoArg
902 (\_ -> do
903 ddir <- getDataDir
904 tpl <- readDataFileUTF8 Nothing "bash_completion.tpl"
905 let optnames (Option shorts longs _ _) =
906 map (\c -> ['-',c]) shorts ++
907 map ("--" ++) longs
908 let allopts = unwords (concatMap optnames options)
909 UTF8.hPutStrLn stdout $ printf tpl allopts
910 (unwords (map fst readers))
911 (unwords ("pdf": map fst writers))
912 ddir
913 exitSuccess ))
914 "" -- "Print bash completion script"
916 , Option "v" ["version"]
917 (NoArg
918 (\_ -> do
919 prg <- getProgName
920 defaultDatadir <- getAppUserDataDirectory "pandoc"
921 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
922 compileInfo ++ "\nDefault user data directory: " ++
923 defaultDatadir ++ copyrightMessage)
924 exitSuccess ))
925 "" -- "Print version"
927 , Option "h" ["help"]
928 (NoArg
929 (\_ -> do
930 prg <- getProgName
931 UTF8.hPutStr stdout (usageMessage prg options)
932 exitSuccess ))
933 "" -- "Show help"
935 ]
938addMetadata :: String -> MetaValue -> M.Map String MetaValue
939 -> M.Map String MetaValue
940addMetadata k v m = case M.lookup k m of
941 Nothing -> M.insert k v m
942 Just (MetaList xs) -> M.insert k
943 (MetaList (xs ++ [v])) m
944 Just x -> M.insert k (MetaList [v, x]) m
946readMetaValue :: String -> MetaValue
947readMetaValue s = case decode (UTF8.fromString s) of
948 Just (Yaml.String t) -> MetaString $ T.unpack t
949 Just (Yaml.Bool b) -> MetaBool b
950 _ -> MetaString s
952-- Returns usage message
953usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
954usageMessage programName = usageInfo
955 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
956 wrapWords 16 78 readers'names ++
957 '\n' : replicate 16 ' ' ++
958 "[* only Pandoc's JSON version of native AST]" ++ "\nOutput formats: " ++
959 wrapWords 16 78 writers'names ++
960 '\n' : replicate 16 ' ' ++
961 "[** for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
962 where
963 writers'names = sort $ "json*" : "pdf**" : delete "json" (map fst writers)
964 readers'names = sort $ "json*" : delete "json" (map fst readers)
966-- Determine default reader based on source file extensions
967defaultReaderName :: String -> [FilePath] -> String
968defaultReaderName fallback [] = fallback
969defaultReaderName fallback (x:xs) =
970 case takeExtension (map toLower x) of
971 ".xhtml" -> "html"
972 ".html" -> "html"
973 ".htm" -> "html"
974 ".tex" -> "latex"
975 ".latex" -> "latex"
976 ".ltx" -> "latex"
977 ".rst" -> "rst"
978 ".org" -> "org"
979 ".lhs" -> "markdown+lhs"
980 ".db" -> "docbook"
981 ".opml" -> "opml"
982 ".wiki" -> "mediawiki"
983 ".dokuwiki" -> "dokuwiki"
984 ".textile" -> "textile"
985 ".native" -> "native"
986 ".json" -> "json"
987 ".docx" -> "docx"
988 ".t2t" -> "t2t"
989 ".epub" -> "epub"
990 ".odt" -> "odt"
991 ".pdf" -> "pdf" -- so we get an "unknown reader" error
992 ".doc" -> "doc" -- so we get an "unknown reader" error
993 _ -> defaultReaderName fallback xs
995-- Returns True if extension of first source is .lhs
996lhsExtension :: [FilePath] -> Bool
997lhsExtension (x:_) = takeExtension x == ".lhs"
998lhsExtension _ = False
1000-- Determine default writer based on output file extension
1001defaultWriterName :: FilePath -> String
1002defaultWriterName "-" = "html" -- no output file
1003defaultWriterName x =
1004 case takeExtension (map toLower x) of
1005 "" -> "markdown" -- empty extension
1006 ".tex" -> "latex"
1007 ".latex" -> "latex"
1008 ".ltx" -> "latex"
1009 ".context" -> "context"
1010 ".ctx" -> "context"
1011 ".rtf" -> "rtf"
1012 ".rst" -> "rst"
1013 ".s5" -> "s5"
1014 ".native" -> "native"
1015 ".json" -> "json"
1016 ".txt" -> "markdown"
1017 ".text" -> "markdown"
1018 ".md" -> "markdown"
1019 ".markdown" -> "markdown"
1020 ".textile" -> "textile"
1021 ".lhs" -> "markdown+lhs"
1022 ".texi" -> "texinfo"
1023 ".texinfo" -> "texinfo"
1024 ".db" -> "docbook"
1025 ".odt" -> "odt"
1026 ".docx" -> "docx"
1027 ".epub" -> "epub"
1028 ".org" -> "org"
1029 ".asciidoc" -> "asciidoc"
1030 ".adoc" -> "asciidoc"
1031 ".pdf" -> "latex"
1032 ".fb2" -> "fb2"
1033 ".opml" -> "opml"
1034 ".icml" -> "icml"
1035 ".tei.xml" -> "tei"
1036 ".tei" -> "tei"
1037 ['.',y] | y `elem` ['1'..'9'] -> "man"
1038 _ -> "html"
1040-- Transformations of a Pandoc document post-parsing:
1042extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1043extractMedia media dir d =
1044 case [fp | (fp, _, _) <- mediaDirectory media] of
1045 [] -> return d
1046 fps -> do
1047 extractMediaBag True dir media
1048 return $ walk (adjustImagePath dir fps) d
1050adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline
1051adjustImagePath dir paths (Image attr lab (src, tit))
1052 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
1053adjustImagePath _ _ x = x
1055adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1056adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
1058applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1059applyTransforms transforms d = return $ foldr ($) d transforms
1061 -- First we check to see if a filter is a path. If it isn't, we
1062 -- check to see whether it's in `userdir/filters`. If not, we leave
1063 -- it unchanged.
1064expandFilterPath :: Maybe FilePath -> FilePath -> IO FilePath
1065expandFilterPath mbDatadir fp
1066 | '/' `elem` fp = return fp
1067 | Just datadir <- mbDatadir = do
1068 let filterPath = (datadir </> "filters" </> fp)
1069 filterPathExists <- doesFileExist filterPath
1070 if filterPathExists
1071 then return filterPath
1072 else return fp
1073 | otherwise = return fp
1075applyFilters :: Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> IO Pandoc
1076applyFilters mbDatadir filters args d = do
1077 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
1078 foldrM ($) d $ map (flip externalFilter args) expandedFilters
1080uppercaseFirstLetter :: String -> String
1081uppercaseFirstLetter (c:cs) = toUpper c : cs
1082uppercaseFirstLetter [] = []
1084main :: IO ()
1085main = do
1087 rawArgs <- map UTF8.decodeArg <$> getArgs
1088 prg <- getProgName
1090 let (actions, args, errors) = getOpt Permute options rawArgs
1092 unless (null errors) $
1093 err 2 $ concat $ errors ++
1094 ["Try " ++ prg ++ " --help for more information."]
1096 -- thread option data structure through all supplied option actions
1097 opts <- foldl (>>=) (return defaultOpts) actions
1098 convertWithOpts opts args
1100convertWithOpts :: Opt -> [FilePath] -> IO ()
1101convertWithOpts opts args = do
1102 let Opt { optTabStop = tabStop
1103 , optPreserveTabs = preserveTabs
1104 , optStandalone = standalone
1105 , optReader = readerName
1106 , optWriter = writerName
1107 , optParseRaw = parseRaw
1108 , optVariables = variables
1109 , optMetadata = metadata
1110 , optTableOfContents = toc
1111 , optTransforms = transforms
1112 , optTemplate = templatePath
1113 , optOutputFile = outputFile
1114 , optNumberSections = numberSections
1115 , optNumberOffset = numberFrom
1116 , optSectionDivs = sectionDivs
1117 , optIncremental = incremental
1118 , optSelfContained = selfContained
1119 , optSmart = smart
1120 , optOldDashes = oldDashes
1121 , optHtml5 = html5
1122 , optHtmlQTags = htmlQTags
1123 , optHighlight = highlight
1124 , optHighlightStyle = highlightStyle
1125 , optChapters = chapters
1126 , optHTMLMathMethod = mathMethod'
1127 , optReferenceODT = referenceODT
1128 , optReferenceDocx = referenceDocx
1129 , optEpubStylesheet = epubStylesheet
1130 , optEpubMetadata = epubMetadata
1131 , optEpubFonts = epubFonts
1132 , optEpubChapterLevel = epubChapterLevel
1133 , optTOCDepth = epubTOCDepth
1134 , optDumpArgs = dumpArgs
1135 , optIgnoreArgs = ignoreArgs
1136 , optVerbose = verbose
1137 , optReferenceLinks = referenceLinks
1138 , optReferenceLocation = referenceLocation
1139 , optDpi = dpi
1140 , optWrapText = wrap
1141 , optColumns = columns
1142 , optFilters = filters
1143 , optEmailObfuscation = obfuscationMethod
1144 , optIdentifierPrefix = idPrefix
1145 , optIndentedCodeClasses = codeBlockClasses
1146 , optDataDir = mbDataDir
1147 , optCiteMethod = citeMethod
1148 , optListings = listings
1149 , optLaTeXEngine = latexEngine
1150 , optLaTeXEngineArgs = latexEngineArgs
1151 , optSlideLevel = slideLevel
1152 , optSetextHeaders = setextHeaders
1153 , optAscii = ascii
1154 , optTeXLigatures = texLigatures
1155 , optDefaultImageExtension = defaultImageExtension
1156 , optExtractMedia = mbExtractMedia
1157 , optTrace = trace
1158 , optTrackChanges = trackChanges
1159 , optFileScope = fileScope
1160 , optKaTeXStylesheet = katexStylesheet
1161 , optKaTeXJS = katexJS
1162 } = opts
1164 when dumpArgs $
1165 do UTF8.hPutStrLn stdout outputFile
1166 mapM_ (UTF8.hPutStrLn stdout) args
1167 exitSuccess
1169 let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
1170 let mathMethod =
1171 case (katexJS, katexStylesheet) of
1172 (Nothing, _) -> mathMethod'
1173 (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1176 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1177 let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1178 optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1179 "pandoc-citeproc" `notElem` map takeBaseName filters
1180 let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1181 else filters
1183 let sources = if ignoreArgs then [] else args
1185 datadir <- case mbDataDir of
1186 Nothing -> E.catch
1187 (Just <$> getAppUserDataDirectory "pandoc")
1188 (\e -> let _ = (e :: E.SomeException)
1189 in return Nothing)
1190 Just _ -> return mbDataDir
1192 -- assign reader and writer based on options and filenames
1193 let readerName' = case map toLower readerName of
1194 [] -> defaultReaderName
1195 (if any isURI sources
1196 then "html"
1197 else "markdown") sources
1198 "html4" -> "html"
1199 x -> x
1201 let writerName' = case map toLower writerName of
1202 [] -> defaultWriterName outputFile
1203 "epub2" -> "epub"
1204 "html4" -> "html"
1205 x -> x
1206 let format = takeWhile (`notElem` ['+','-'])
1207 $ takeFileName writerName' -- in case path to lua script
1209 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1211 let laTeXOutput = format `elem` ["latex", "beamer"]
1212 let conTeXtOutput = format == "context"
1213 let html5Output = format == "html5"
1215 let laTeXInput = "latex" `isPrefixOf` readerName' ||
1216 "beamer" `isPrefixOf` readerName'
1218 writer <- if ".lua" `isSuffixOf` format
1219 -- note: use non-lowercased version writerName
1220 then return $ IOStringWriter $ writeCustom writerName
1221 else case getWriter writerName' of
1222 Left e -> err 9 $
1223 if format == "pdf"
1224 then e ++
1225 "\nTo create a pdf with pandoc, use " ++
1226 "the latex or beamer writer and specify\n" ++
1227 "an output file with .pdf extension " ++
1228 "(pandoc -t latex -o filename.pdf)."
1229 else e
1230 Right w -> return w
1232 reader <- if "t2t" == readerName'
1233 then (mkStringReader .
1234 readTxt2Tags) <$>
1235 getT2TMeta sources outputFile
1236 else case getReader readerName' of
1237 Right r -> return r
1238 Left e -> err 7 e'
1239 where e' = case readerName' of
1240 "pdf" -> e ++
1241 "\nPandoc can convert to PDF, but not from PDF."
1242 "doc" -> e ++
1243 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1244 _ -> e
1246 let standalone' = standalone || not (isTextFormat format) || pdfOutput
1248 templ <- case templatePath of
1249 _ | not standalone' -> return ""
1250 Nothing -> do
1251 deftemp <- getDefaultTemplate datadir format
1252 case deftemp of
1253 Left e -> throwIO e
1254 Right t -> return t
1255 Just tp -> do
1256 -- strip off extensions
1257 let tp' = case takeExtension tp of
1258 "" -> tp <.> format
1259 _ -> tp
1260 E.catch (UTF8.readFile tp')
1261 (\e -> if isDoesNotExistError e
1262 then E.catch
1263 (readDataFileUTF8 datadir
1264 ("templates" </> tp'))
1265 (\e' -> let _ = (e' :: E.SomeException)
1266 in throwIO e')
1267 else throwIO e)
1269 variables' <- case mathMethod of
1270 LaTeXMathML Nothing -> do
1271 s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1272 return $ ("mathml-script", s) : variables
1273 MathML Nothing -> do
1274 s <- readDataFileUTF8 datadir "MathMLinHTML.js"
1275 return $ ("mathml-script", s) : variables
1276 _ -> return variables
1278 variables'' <- if format == "dzslides"
1279 then do
1280 dztempl <- readDataFileUTF8 datadir
1281 ("dzslides" </> "template.html")
1282 let dzline = "<!-- {{{{ dzslides core"
1283 let dzcore = unlines
1284 $ dropWhile (not . (dzline `isPrefixOf`))
1285 $ lines dztempl
1286 return $ ("dzslides-core", dzcore) : variables'
1287 else return variables'
1289 let sourceURL = case sources of
1290 [] -> Nothing
1291 (x:_) -> case parseURI x of
1292 Just u
1293 | uriScheme u `elem` ["http:","https:"] ->
1294 Just $ show u{ uriQuery = "",
1295 uriFragment = "" }
1296 _ -> Nothing
1298 let readerOpts = def{ readerSmart = if laTeXInput
1299 then texLigatures
1300 else smart || (texLigatures &&
1301 (laTeXOutput || conTeXtOutput))
1302 , readerStandalone = standalone'
1303 , readerParseRaw = parseRaw
1304 , readerColumns = columns
1305 , readerTabStop = tabStop
1306 , readerOldDashes = oldDashes
1307 , readerIndentedCodeClasses = codeBlockClasses
1308 , readerApplyMacros = not laTeXOutput
1309 , readerDefaultImageExtension = defaultImageExtension
1310 , readerTrace = trace
1311 , readerTrackChanges = trackChanges
1312 , readerFileScope = fileScope
1315 when (not (isTextFormat format) && outputFile == "-") $
1316 err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1317 "Specify an output file using the -o option."
1319 let readSources [] = mapM readSource ["-"]
1320 readSources srcs = mapM readSource srcs
1321 readSource "-" = UTF8.getContents
1322 readSource src = case parseURI src of
1323 Just u | uriScheme u `elem` ["http:","https:"] ->
1324 readURI src
1325 _ -> UTF8.readFile src
1326 readURI src = do
1327 res <- openURL src
1328 case res of
1329 Left e -> throwIO e
1330 Right (bs,_) -> return $ UTF8.toString bs
1332 let readFiles [] = error "Cannot read archive from stdin"
1333 readFiles [x] = B.readFile x
1334 readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1336 let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1337 then 0
1338 else tabStop)
1340 let handleIncludes' :: String -> IO (Either PandocError String)
1341 handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1342 then handleIncludes
1343 else return . Right
1345 let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1346 sourceToDoc sources' = fmap handleError $
1347 case reader of
1348 StringReader r-> do
1349 srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1350 doc <- handleIncludes' srcs
1351 either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1352 ByteStringReader r -> readFiles sources' >>= r readerOpts
1354 -- We parse first if (1) fileScope is set, (2), it's a binary
1355 -- reader, or (3) we're reading JSON. This is easier to do of an AND
1356 -- of negatives as opposed to an OR of positives, so we do default
1357 -- parsing if it's a StringReader AND (fileScope is set AND it's not
1358 -- a JSON reader).
1359 (doc, media) <- case reader of
1360 (StringReader _) | not fileScope && readerName' /= "json" ->
1361 sourceToDoc sources
1362 _ | null sources -> sourceToDoc sources
1363 _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1364 return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1366 let writerOptions = def { writerStandalone = standalone',
1367 writerTemplate = templ,
1368 writerVariables = variables'',
1369 writerTabStop = tabStop,
1370 writerTableOfContents = toc,
1371 writerHTMLMathMethod = mathMethod,
1372 writerIncremental = incremental,
1373 writerCiteMethod = citeMethod,
1374 writerIgnoreNotes = False,
1375 writerNumberSections = numberSections,
1376 writerNumberOffset = numberFrom,
1377 writerSectionDivs = sectionDivs,
1378 writerReferenceLinks = referenceLinks,
1379 writerReferenceLocation = referenceLocation,
1380 writerDpi = dpi,
1381 writerWrapText = wrap,
1382 writerColumns = columns,
1383 writerEmailObfuscation = obfuscationMethod,
1384 writerIdentifierPrefix = idPrefix,
1385 writerSourceURL = sourceURL,
1386 writerUserDataDir = datadir,
1387 writerHtml5 = html5,
1388 writerHtmlQTags = htmlQTags,
1389 writerChapters = chapters,
1390 writerListings = listings,
1391 writerBeamer = False,
1392 writerSlideLevel = slideLevel,
1393 writerHighlight = highlight,
1394 writerHighlightStyle = highlightStyle,
1395 writerSetextHeaders = setextHeaders,
1396 writerTeXLigatures = texLigatures,
1397 writerEpubMetadata = epubMetadata,
1398 writerEpubStylesheet = epubStylesheet,
1399 writerEpubFonts = epubFonts,
1400 writerEpubChapterLevel = epubChapterLevel,
1401 writerTOCDepth = epubTOCDepth,
1402 writerReferenceODT = referenceODT,
1403 writerReferenceDocx = referenceDocx,
1404 writerMediaBag = media,
1405 writerVerbose = verbose,
1406 writerLaTeXArgs = latexEngineArgs
1410 doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1411 adjustMetadata metadata >=>
1412 applyTransforms transforms >=>
1413 applyFilters datadir filters' [format]) doc
1415 let writeBinary :: B.ByteString -> IO ()
1416 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1418 let writerFn :: FilePath -> String -> IO ()
1419 writerFn "-" = UTF8.putStr
1420 writerFn f = UTF8.writeFile f
1422 case writer of
1423 IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1424 IOByteStringWriter f -> f writerOptions doc' >>= writeBinary
1425 PureStringWriter f
1426 | pdfOutput -> do
1427 -- make sure writer is latex or beamer or context or html5
1428 unless (laTeXOutput || conTeXtOutput || html5Output) $
1429 err 47 $ "cannot produce pdf output with " ++ format ++
1430 " writer"
1432 let pdfprog = case () of
1433 _ | conTeXtOutput -> "context"
1434 _ | html5Output -> "wkhtmltopdf"
1435 _ -> latexEngine
1436 -- check for pdf creating program
1437 mbPdfProg <- findExecutable pdfprog
1438 when (isNothing mbPdfProg) $
1439 err 41 $ pdfprog ++ " not found. " ++
1440 pdfprog ++ " is needed for pdf output."
1442 res <- makePDF pdfprog f writerOptions doc'
1443 case res of
1444 Right pdf -> writeBinary pdf
1445 Left err' -> do
1446 B.hPutStr stderr err'
1447 B.hPut stderr $ B.pack [10]
1448 err 43 "Error producing PDF"
1449 | otherwise -> selfcontain (f writerOptions doc' ++
1450 ['\n' | not standalone'])
1451 >>= writerFn outputFile . handleEntities
1452 where htmlFormat = format `elem`
1453 ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1454 selfcontain = if selfContained && htmlFormat
1455 then makeSelfContained writerOptions
1456 else return
1457 handleEntities = if htmlFormat && ascii
1458 then toEntities
1459 else id