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