git.iko.soy/typeable/pandoc
on master help sign in
App: extract output settings into module
commit418bd42df85b93016e50ba48042804e8f51341b5 authorAlbert Krewinkel <albert@zeitkraut.de> dateSat 3 Nov 2018, 07:33 (8 years ago) parentfd3c8cd
4 files changed+348−204
pandoc.cabal+1 src/Text/Pandoc/App.hs+28−201 src/Text/Pandoc/App/CommandLineOptions.hs+2−3 src/Text/Pandoc/App/OutputSettings.hs+317
pandoc.cabal+1
@@ -515,6 +515,7 @@ library
515515 Text.Pandoc.BCP47,
516516 Text.Pandoc.Class
517517 other-modules: Text.Pandoc.App.CommandLineOptions,
518+ Text.Pandoc.App.OutputSettings,
518519 Text.Pandoc.Filter.JSON,
519520 Text.Pandoc.Filter.Lua,
520521 Text.Pandoc.Filter.Path,
src/Text/Pandoc/App.hs+28−201
@@ -1,7 +1,6 @@
11{-# LANGUAGE NoImplicitPrelude #-}
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE TupleSections #-}
54{-
65Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu>
76
@@ -44,12 +43,10 @@ module Text.Pandoc.App (
4443import Prelude
4544import qualified Control.Exception as E
4645import Control.Monad
47−import Control.Monad.Except (catchError, throwError)
4846import Control.Monad.Trans
4947import qualified Data.ByteString as BS
5048import qualified Data.ByteString.Lazy as B
5149import Data.Char (toLower)
52−import Data.List (find, isPrefixOf, isSuffixOf)
5350import Data.Maybe (fromMaybe, isJust, isNothing)
5451import qualified Data.Set as Set
5552import Data.Text (Text)
@@ -59,8 +56,6 @@ import qualified Data.Text.Lazy.Encoding as TE
5956import qualified Data.Text.Encoding.Error as TE
6057import qualified Data.YAML as YAML
6158import Network.URI (URI (..), parseURI)
62−import Skylighting (defaultSyntaxMap)
63−import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition)
6459import System.Directory (getAppUserDataDirectory)
6560import System.Exit (exitSuccess)
6661import System.FilePath
@@ -68,7 +63,8 @@ import System.IO (nativeNewline, stdout)
6863import qualified System.IO as IO (Newline (..))
6964import Text.Pandoc
7065import Text.Pandoc.App.CommandLineOptions (Opt (..), LineEnding (..),
71− defaultOpts, engines, parseOptions, options)
66+ defaultOpts, parseOptions, options)
67+import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings)
7268import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
7369import Text.Pandoc.Builder (setMeta, deleteMeta)
7470import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters)
@@ -83,41 +79,6 @@ import System.Posix.IO (stdOutput)
8379import System.Posix.Terminal (queryTerminal)
8480#endif
8581
86−pdfIsNoWriterErrorMsg :: String
87−pdfIsNoWriterErrorMsg =
88− "To create a pdf using pandoc, use " ++
89− "-t latex|beamer|context|ms|html5" ++
90− "\nand specify an output file with " ++
91− ".pdf extension (-o filename.pdf)."
92−
93−pdfWriterAndProg :: Maybe String -- ^ user-specified writer name
94− -> Maybe String -- ^ user-specified pdf-engine
95− -> IO (String, Maybe String) -- ^ IO (writerName, maybePdfEngineProg)
96−pdfWriterAndProg mWriter mEngine = do
97− let panErr msg = liftIO $ E.throwIO $ PandocAppError msg
98− case go mWriter mEngine of
99− Right (writ, prog) -> return (writ, Just prog)
100− Left err -> panErr err
101− where
102− go Nothing Nothing = Right ("latex", "pdflatex")
103− go (Just writer) Nothing = (writer,) <$> engineForWriter writer
104− go Nothing (Just engine) = (,engine) <$> writerForEngine (takeBaseName engine)
105− go (Just writer) (Just engine) =
106− case find (== (baseWriterName writer, takeBaseName engine)) engines of
107− Just _ -> Right (writer, engine)
108− Nothing -> Left $ "pdf-engine " ++ engine ++
109− " is not compatible with output format " ++ writer
110−
111− writerForEngine eng = case [f | (f,e) <- engines, e == eng] of
112− fmt : _ -> Right fmt
113− [] -> Left $
114− "pdf-engine " ++ eng ++ " not known"
115−
116− engineForWriter "pdf" = Left pdfIsNoWriterErrorMsg
117− engineForWriter w = case [e | (f,e) <- engines, f == baseWriterName w] of
118− eng : _ -> Right eng
119− [] -> Left $
120− "cannot produce pdf output from " ++ w
12182
12283convertWithOpts :: Opt -> IO ()
12384convertWithOpts opts = do
@@ -130,10 +91,6 @@ convertWithOpts opts = do
13091 mapM_ (UTF8.hPutStrLn stdout) (optInputFiles opts)
13192 exitSuccess
13293
133− epubMetadata <- case optEpubMetadata opts of
134− Nothing -> return Nothing
135− Just fp -> Just <$> UTF8.readFile fp
136−
13794 let isPandocCiteproc (JSONFilter f) = takeBaseName f == "pandoc-citeproc"
13895 isPandocCiteproc _ = False
13996 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
@@ -165,30 +122,6 @@ convertWithOpts opts = do
165122
166123 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
167124
168− (writerName, maybePdfProg) <-
169− if pdfOutput
170− then pdfWriterAndProg (optWriter opts) (optPdfEngine opts)
171− else case optWriter opts of
172− Nothing ->
173− return (formatFromFilePaths "html" [outputFile], Nothing)
174− Just f -> return (f, Nothing)
175−
176− let format = map toLower $ baseWriterName
177− $ takeFileName writerName -- in case path to lua script
178−
179− -- disabling the custom writer for now
180− (writer, writerExts) <-
181− if ".lua" `isSuffixOf` format
182− then return (TextWriter
183− (\o d -> writeCustom writerName o d)
184− :: Writer PandocIO, mempty)
185− else case getWriter (map toLower writerName) of
186− Left e -> E.throwIO $ PandocAppError $
187− if format == "pdf"
188− then e ++ "\n" ++ pdfIsNoWriterErrorMsg
189− else e
190− Right (w, es) -> return (w :: Writer PandocIO, es)
191−
192125 -- TODO: we have to get the input and the output into the state for
193126 -- the sake of the text2tags reader.
194127 (reader, readerExts) <-
@@ -202,34 +135,6 @@ convertWithOpts opts = do
202135 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
203136 _ -> e
204137
205− let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
206− let addStringAsVariable varname s vars = return $ (varname, s) : vars
207−
208− let addSyntaxMap existingmap f = do
209− res <- parseSyntaxDefinition f
210− case res of
211− Left errstr -> E.throwIO $ PandocSyntaxMapError errstr
212− Right syn -> return $ addSyntaxDefinition syn existingmap
213−
214− syntaxMap <- foldM addSyntaxMap defaultSyntaxMap
215− (optSyntaxDefinitions opts)
216−
217− -- We don't want to send output to the terminal if the user
218− -- does 'pandoc -t docx input.txt'; though we allow them to
219− -- force this with '-o -'. On posix systems, we detect
220− -- when stdout is being piped and allow output to stdout
221− -- in that case, but on Windows we can't.
222−#ifdef _WINDOWS
223− let istty = True
224−#else
225− istty <- queryTerminal stdOutput
226−#endif
227− when (not (isTextFormat format) && istty && isNothing ( optOutputFile opts)) $
228− E.throwIO $ PandocAppError $
229− "Cannot write " ++ format ++ " output to terminal.\n" ++
230− "Specify an output file using the -o option, or " ++
231− "use '-o -' to force output to stdout."
232−
233138 let convertTabs = tabFilter (if optPreserveTabs opts ||
234139 readerName == "t2t" ||
235140 readerName == "man"
@@ -261,80 +166,41 @@ convertWithOpts opts = do
261166 LF -> IO.LF
262167 Native -> nativeNewline
263168
264− -- note: this reverses the list constructed in option parsing,
265− -- which in turn was reversed from the command-line order,
266− -- so we end up with the correct order in the variable list:
267− let withList _ [] vars = return vars
268− withList f (x:xs) vars = f x vars >>= withList f xs
269−
270− let addContentsAsVariable varname fp vars = do
271− s <- UTF8.toString <$> readFileStrict fp
272− return $ (varname, s) : vars
273−
274169 runIO' $ do
275170 setUserDataDir datadir
276171 setInputFiles (optInputFiles opts)
277172 setOutputFile (optOutputFile opts)
278173
279− variables <-
280− withList (addStringAsVariable "sourcefile")
281− (reverse $ optInputFiles opts)
282− (("outputfile", fromMaybe "-" (optOutputFile opts))
283− : optVariables opts)
284− -- we reverse this list because, unlike
285− -- the other option lists here, it is
286− -- not reversed when parsed from CLI arguments.
287− -- See withList, above.
288− >>=
289− withList (addContentsAsVariable "include-before")
290− (optIncludeBeforeBody opts)
291− >>=
292− withList (addContentsAsVariable "include-after")
293− (optIncludeAfterBody opts)
294− >>=
295− withList (addContentsAsVariable "header-includes")
296− (optIncludeInHeader opts)
297− >>=
298− withList (addStringAsVariable "css") (optCss opts)
299− >>=
300− maybe return (addStringAsVariable "title-prefix")
301− (optTitlePrefix opts)
302− >>=
303− maybe return (addStringAsVariable "epub-cover-image")
304− (optEpubCoverImage opts)
305− >>=
306− (\vars -> if format == "dzslides"
307− then do
308− dztempl <- UTF8.toString <$> readDataFile
309− ("dzslides" </> "template.html")
310− let dzline = "<!-- {{{{ dzslides core"
311− let dzcore = unlines
312− $ dropWhile (not . (dzline `isPrefixOf`))
313− $ lines dztempl
314− return $ ("dzslides-core", dzcore) : vars
315− else return vars)
174+ outputSettings <- optToOutputSettings opts
175+ let format = outputFormat outputSettings
176+ let writer = outputWriter outputSettings
177+ let writerName = outputWriterName outputSettings
178+ let writerOptions = outputWriterOptions outputSettings
179+
180+ let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
181+
182+ -- We don't want to send output to the terminal if the user
183+ -- does 'pandoc -t docx input.txt'; though we allow them to
184+ -- force this with '-o -'. On posix systems, we detect
185+ -- when stdout is being piped and allow output to stdout
186+ -- in that case, but on Windows we can't.
187+#ifdef _WINDOWS
188+ let istty = True
189+#else
190+ istty <- liftIO $ queryTerminal stdOutput
191+#endif
192+ when (not (isTextFormat format) && istty && isNothing ( optOutputFile opts)) $
193+ liftIO $ E.throwIO $ PandocAppError $
194+ "Cannot write " ++ format ++ " output to terminal.\n" ++
195+ "Specify an output file using the -o option, or " ++
196+ "use '-o -' to force output to stdout."
197+
316198
317199 abbrevs <- Set.fromList . filter (not . null) . lines <$>
318200 case optAbbreviations opts of
319201 Nothing -> UTF8.toString <$> readDataFile "abbreviations"
320202 Just f -> UTF8.toString <$> readFileStrict f
321203
322− templ <- case optTemplate opts of
323− _ | not standalone -> return Nothing
324− Nothing -> Just <$> getDefaultTemplate format
325− Just tp -> do
326− -- strip off extensions
327− let tp' = case takeExtension tp of
328− "" -> tp <.> format
329− _ -> tp
330− Just . UTF8.toString <$>
331− ((fst <$> fetchItem tp') `catchError`
332− (\e ->
333− case e of
334− PandocResourceNotFound _ ->
335− readDataFile ("templates" </> tp')
336− _ -> throwError e))
337−
338204 metadata <- if format == "jats" &&
339205 isNothing (lookup "csl" (optMetadata opts)) &&
340206 isNothing (lookup "citation-style" (optMetadata opts))
@@ -355,41 +221,6 @@ convertWithOpts opts = do
355221 Right l' -> setTranslations l'
356222 Nothing -> setTranslations $ Lang "en" "" "US" []
357223
358− let writerOptions = def {
359− writerTemplate = templ
360− , writerVariables = variables
361− , writerTabStop = optTabStop opts
362− , writerTableOfContents = optTableOfContents opts
363− , writerHTMLMathMethod = optHTMLMathMethod opts
364− , writerIncremental = optIncremental opts
365− , writerCiteMethod = optCiteMethod opts
366− , writerNumberSections = optNumberSections opts
367− , writerNumberOffset = optNumberOffset opts
368− , writerSectionDivs = optSectionDivs opts
369− , writerExtensions = writerExts
370− , writerReferenceLinks = optReferenceLinks opts
371− , writerReferenceLocation = optReferenceLocation opts
372− , writerDpi = optDpi opts
373− , writerWrapText = optWrapText opts
374− , writerColumns = optColumns opts
375− , writerEmailObfuscation = optEmailObfuscation opts
376− , writerIdentifierPrefix = optIdentifierPrefix opts
377− , writerHtmlQTags = optHtmlQTags opts
378− , writerTopLevelDivision = optTopLevelDivision opts
379− , writerListings = optListings opts
380− , writerSlideLevel = optSlideLevel opts
381− , writerHighlightStyle = optHighlightStyle opts
382− , writerSetextHeaders = optSetextHeaders opts
383− , writerEpubSubdirectory = optEpubSubdirectory opts
384− , writerEpubMetadata = epubMetadata
385− , writerEpubFonts = optEpubFonts opts
386− , writerEpubChapterLevel = optEpubChapterLevel opts
387− , writerTOCDepth = optTOCDepth opts
388− , writerReferenceDoc = optReferenceDoc opts
389− , writerSyntaxMap = syntaxMap
390− , writerPreferAscii = optAscii opts
391− }
392−
393224 let readerOpts = def{
394225 readerStandalone = standalone
395226 , readerColumns = optColumns opts
@@ -412,7 +243,7 @@ convertWithOpts opts = do
412243 (if extensionEnabled Ext_east_asian_line_breaks
413244 readerExts &&
414245 not (extensionEnabled Ext_east_asian_line_breaks
415− writerExts &&
246+ (writerExtensions writerOptions) &&
416247 writerWrapText writerOptions == WrapPreserve)
417248 then (eastAsianLineBreakFilter :)
418249 else id) $
@@ -450,7 +281,7 @@ convertWithOpts opts = do
450281
451282 case writer of
452283 ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile
453− TextWriter f -> case maybePdfProg of
284+ TextWriter f -> case outputPdfProgram outputSettings of
454285 Just pdfProg -> do
455286 res <- makePDF pdfProg (optPdfEngineArgs opts) f
456287 writerOptions doc
@@ -594,7 +425,3 @@ writerFn :: MonadIO m => IO.Newline -> FilePath -> Text -> m ()
594425-- TODO this implementation isn't maximally efficient:
595426writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack
596427writerFn eol f = liftIO . UTF8.writeFileWith eol f . T.unpack
597−
598−
599−baseWriterName :: String -> String
600−baseWriterName = takeWhile (\c -> c /= '+' && c /= '-')
src/Text/Pandoc/App/CommandLineOptions.hs+2−3
@@ -268,9 +268,8 @@ defaultOpts = Opt
268268 , optStripComments = False
269269 }
270270
271−lookupHighlightStyle :: Maybe String -> IO (Maybe Style)
272−lookupHighlightStyle Nothing = return Nothing
273−lookupHighlightStyle (Just s)
271+lookupHighlightStyle :: String -> IO (Maybe Style)
272+lookupHighlightStyle s
274273 | takeExtension s == ".theme" = -- attempt to load KDE theme
275274 do contents <- B.readFile s
276275 case parseTheme contents of
src/Text/Pandoc/App/OutputSettings.hsnew file+317
@@ -0,0 +1,317 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE TupleSections #-}
6+{-
7+Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu>
8+
9+This program is free software; you can redistribute it and/or modify
10+it under the terms of the GNU General Public License as published by
11+the Free Software Foundation; either version 2 of the License, or
12+(at your option) any later version.
13+
14+This program is distributed in the hope that it will be useful,
15+but WITHOUT ANY WARRANTY; without even the implied warranty of
16+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+GNU General Public License for more details.
18+
19+You should have received a copy of the GNU General Public License
20+along with this program; if not, write to the Free Software
21+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22+-}
23+
24+{- |
25+ Module : Text.Pandoc.App
26+ Copyright : Copyright (C) 2006-2018 John MacFarlane
27+ License : GNU GPL, version 2 or above
28+
29+ Maintainer : John MacFarlane <jgm@berkeley@edu>
30+ Stability : alpha
31+ Portability : portable
32+
33+Does a pandoc conversion based on command-line options.
34+-}
35+module Text.Pandoc.App.OutputSettings
36+ ( OutputSettings (..)
37+ , optToOutputSettings
38+ ) where
39+import Prelude
40+import qualified Control.Exception as E
41+import Control.Monad
42+import Control.Monad.Except (catchError, throwError)
43+import Control.Monad.Trans
44+import Data.Char (toLower)
45+import Data.List (find, isPrefixOf, isSuffixOf)
46+import Data.Maybe (fromMaybe)
47+import Skylighting (defaultSyntaxMap)
48+import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition)
49+import System.Exit (exitSuccess)
50+import System.FilePath
51+import System.IO (stdout)
52+import Text.Pandoc
53+import Text.Pandoc.App.CommandLineOptions (Opt (..), engines)
54+import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
55+import qualified Text.Pandoc.UTF8 as UTF8
56+
57+-- | Settings specifying how document output should be produced.
58+data OutputSettings = OutputSettings
59+ { outputFormat :: String
60+ , outputWriter :: Writer PandocIO
61+ , outputWriterName :: String
62+ , outputWriterOptions :: WriterOptions
63+ , outputPdfProgram :: Maybe String
64+ }
65+
66+readUtf8File :: PandocMonad m => FilePath -> m String
67+readUtf8File = fmap UTF8.toString . readFileStrict
68+
69+-- | Get output settings from command line options.
70+optToOutputSettings :: Opt -> PandocIO OutputSettings
71+optToOutputSettings opts = do
72+ let outputFile = fromMaybe "-" (optOutputFile opts)
73+
74+ when (optDumpArgs opts) . liftIO $ do
75+ UTF8.hPutStrLn stdout outputFile
76+ mapM_ (UTF8.hPutStrLn stdout) (optInputFiles opts)
77+ exitSuccess
78+
79+ epubMetadata <- case optEpubMetadata opts of
80+ Nothing -> return Nothing
81+ Just fp -> Just <$> readUtf8File fp
82+
83+ let nonPdfWriterName Nothing = defaultWriterName outputFile
84+ nonPdfWriterName (Just x) = x
85+
86+ let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
87+ (writerName, maybePdfProg) <-
88+ if pdfOutput
89+ then liftIO $ pdfWriterAndProg (optWriter opts) (optPdfEngine opts)
90+ else return (nonPdfWriterName $ optWriter opts, Nothing)
91+
92+ let format = map toLower $ baseWriterName
93+ $ takeFileName writerName -- in case path to lua script
94+
95+ -- disabling the custom writer for now
96+ (writer, writerExts) <-
97+ if ".lua" `isSuffixOf` format
98+ then return (TextWriter
99+ (\o d -> writeCustom writerName o d)
100+ :: Writer PandocIO, mempty)
101+ else case getWriter (map toLower writerName) of
102+ Left e -> throwError $ PandocAppError $
103+ if format == "pdf"
104+ then e ++ "\n" ++ pdfIsNoWriterErrorMsg
105+ else e
106+ Right (w, es) -> return (w :: Writer PandocIO, es)
107+
108+
109+ let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
110+
111+ let addStringAsVariable varname s vars = return $ (varname, s) : vars
112+
113+ let addSyntaxMap existingmap f = do
114+ res <- liftIO (parseSyntaxDefinition f)
115+ case res of
116+ Left errstr -> throwError $ PandocSyntaxMapError errstr
117+ Right syn -> return $ addSyntaxDefinition syn existingmap
118+
119+ syntaxMap <- foldM addSyntaxMap defaultSyntaxMap
120+ (optSyntaxDefinitions opts)
121+
122+ -- note: this reverses the list constructed in option parsing,
123+ -- which in turn was reversed from the command-line order,
124+ -- so we end up with the correct order in the variable list:
125+ let withList _ [] vars = return vars
126+ withList f (x:xs) vars = f x vars >>= withList f xs
127+
128+ let addContentsAsVariable varname fp vars = do
129+ s <- UTF8.toString <$> readFileStrict fp
130+ return $ (varname, s) : vars
131+
132+ variables <-
133+ withList (addStringAsVariable "sourcefile")
134+ (reverse $ optInputFiles opts)
135+ (("outputfile", fromMaybe "-" (optOutputFile opts))
136+ : optVariables opts)
137+ -- we reverse this list because, unlike
138+ -- the other option lists here, it is
139+ -- not reversed when parsed from CLI arguments.
140+ -- See withList, above.
141+ >>=
142+ withList (addContentsAsVariable "include-before")
143+ (optIncludeBeforeBody opts)
144+ >>=
145+ withList (addContentsAsVariable "include-after")
146+ (optIncludeAfterBody opts)
147+ >>=
148+ withList (addContentsAsVariable "header-includes")
149+ (optIncludeInHeader opts)
150+ >>=
151+ withList (addStringAsVariable "css") (optCss opts)
152+ >>=
153+ maybe return (addStringAsVariable "title-prefix")
154+ (optTitlePrefix opts)
155+ >>=
156+ maybe return (addStringAsVariable "epub-cover-image")
157+ (optEpubCoverImage opts)
158+ >>=
159+ (\vars -> if format == "dzslides"
160+ then do
161+ dztempl <- UTF8.toString <$> readDataFile
162+ ("dzslides" </> "template.html")
163+ let dzline = "<!-- {{{{ dzslides core"
164+ let dzcore = unlines
165+ $ dropWhile (not . (dzline `isPrefixOf`))
166+ $ lines dztempl
167+ return $ ("dzslides-core", dzcore) : vars
168+ else return vars)
169+
170+ templ <- case optTemplate opts of
171+ _ | not standalone -> return Nothing
172+ Nothing -> Just <$> getDefaultTemplate format
173+ Just tp -> do
174+ -- strip off extensions
175+ let tp' = case takeExtension tp of
176+ "" -> tp <.> format
177+ _ -> tp
178+ Just . UTF8.toString <$>
179+ ((fst <$> fetchItem tp') `catchError`
180+ (\e ->
181+ case e of
182+ PandocResourceNotFound _ ->
183+ readDataFile ("templates" </> tp')
184+ _ -> throwError e))
185+
186+ case lookup "lang" (optMetadata opts) of
187+ Just l -> case parseBCP47 l of
188+ Left _ -> return ()
189+ Right l' -> setTranslations l'
190+ Nothing -> setTranslations $ Lang "en" "" "US" []
191+
192+ let writerOpts = def {
193+ writerTemplate = templ
194+ , writerVariables = variables
195+ , writerTabStop = optTabStop opts
196+ , writerTableOfContents = optTableOfContents opts
197+ , writerHTMLMathMethod = optHTMLMathMethod opts
198+ , writerIncremental = optIncremental opts
199+ , writerCiteMethod = optCiteMethod opts
200+ , writerNumberSections = optNumberSections opts
201+ , writerNumberOffset = optNumberOffset opts
202+ , writerSectionDivs = optSectionDivs opts
203+ , writerExtensions = writerExts
204+ , writerReferenceLinks = optReferenceLinks opts
205+ , writerReferenceLocation = optReferenceLocation opts
206+ , writerDpi = optDpi opts
207+ , writerWrapText = optWrapText opts
208+ , writerColumns = optColumns opts
209+ , writerEmailObfuscation = optEmailObfuscation opts
210+ , writerIdentifierPrefix = optIdentifierPrefix opts
211+ , writerHtmlQTags = optHtmlQTags opts
212+ , writerTopLevelDivision = optTopLevelDivision opts
213+ , writerListings = optListings opts
214+ , writerSlideLevel = optSlideLevel opts
215+ , writerHighlightStyle = optHighlightStyle opts
216+ , writerSetextHeaders = optSetextHeaders opts
217+ , writerEpubSubdirectory = optEpubSubdirectory opts
218+ , writerEpubMetadata = epubMetadata
219+ , writerEpubFonts = optEpubFonts opts
220+ , writerEpubChapterLevel = optEpubChapterLevel opts
221+ , writerTOCDepth = optTOCDepth opts
222+ , writerReferenceDoc = optReferenceDoc opts
223+ , writerSyntaxMap = syntaxMap
224+ , writerPreferAscii = optAscii opts
225+ }
226+ return $ OutputSettings
227+ { outputFormat = format
228+ , outputWriter = writer
229+ , outputWriterName = writerName
230+ , outputWriterOptions = writerOpts
231+ , outputPdfProgram = maybePdfProg
232+ }
233+
234+-- Determine default writer based on output file extension
235+defaultWriterName :: FilePath -> String
236+defaultWriterName "-" = "html" -- no output file
237+defaultWriterName x =
238+ case takeExtension (map toLower x) of
239+ "" -> "markdown" -- empty extension
240+ ".tex" -> "latex"
241+ ".latex" -> "latex"
242+ ".ltx" -> "latex"
243+ ".context" -> "context"
244+ ".ctx" -> "context"
245+ ".rtf" -> "rtf"
246+ ".rst" -> "rst"
247+ ".s5" -> "s5"
248+ ".native" -> "native"
249+ ".json" -> "json"
250+ ".txt" -> "markdown"
251+ ".text" -> "markdown"
252+ ".md" -> "markdown"
253+ ".muse" -> "muse"
254+ ".markdown" -> "markdown"
255+ ".textile" -> "textile"
256+ ".lhs" -> "markdown+lhs"
257+ ".texi" -> "texinfo"
258+ ".texinfo" -> "texinfo"
259+ ".db" -> "docbook"
260+ ".odt" -> "odt"
261+ ".docx" -> "docx"
262+ ".epub" -> "epub"
263+ ".org" -> "org"
264+ ".asciidoc" -> "asciidoc"
265+ ".adoc" -> "asciidoc"
266+ ".fb2" -> "fb2"
267+ ".opml" -> "opml"
268+ ".icml" -> "icml"
269+ ".tei.xml" -> "tei"
270+ ".tei" -> "tei"
271+ ".ms" -> "ms"
272+ ".roff" -> "ms"
273+ ".pptx" -> "pptx"
274+ ['.',y] | y `elem` ['1'..'9'] -> "man"
275+ _ -> "html"
276+
277+baseWriterName :: String -> String
278+baseWriterName = takeWhile (\c -> c /= '+' && c /= '-')
279+
280+pdfIsNoWriterErrorMsg :: String
281+pdfIsNoWriterErrorMsg =
282+ "To create a pdf using pandoc, use " ++
283+ "-t latex|beamer|context|ms|html5" ++
284+ "\nand specify an output file with " ++
285+ ".pdf extension (-o filename.pdf)."
286+
287+pdfWriterAndProg :: Maybe String -- ^ user-specified writer name
288+ -> Maybe String -- ^ user-specified pdf-engine
289+ -> IO (String, Maybe String) -- ^ IO (writerName, maybePdfEngineProg)
290+pdfWriterAndProg mWriter mEngine = do
291+ let panErr msg = liftIO $ E.throwIO $ PandocAppError msg
292+ case go mWriter mEngine of
293+ Right (writ, prog) -> return (writ, Just prog)
294+ Left err -> panErr err
295+ where
296+ go Nothing Nothing = Right ("latex", "pdflatex")
297+ go (Just writer) Nothing = (writer,) <$> engineForWriter writer
298+ go Nothing (Just engine) = (,engine) <$> writerForEngine (takeBaseName engine)
299+ go (Just writer) (Just engine) =
300+ case find (== (baseWriterName writer, takeBaseName engine)) engines of
301+ Just _ -> Right (writer, engine)
302+ Nothing -> Left $ "pdf-engine " ++ engine ++
303+ " is not compatible with output format " ++ writer
304+
305+ writerForEngine eng = case [f | (f,e) <- engines, e == eng] of
306+ fmt : _ -> Right fmt
307+ [] -> Left $
308+ "pdf-engine " ++ eng ++ " not known"
309+
310+ engineForWriter "pdf" = Left pdfIsNoWriterErrorMsg
311+ engineForWriter w = case [e | (f,e) <- engines, f == baseWriterName w] of
312+ eng : _ -> Right eng
313+ [] -> Left $
314+ "cannot produce pdf output from " ++ w
315+
316+isTextFormat :: String -> Bool
317+isTextFormat s = s `notElem` ["odt","docx","epub2","epub3","epub","pptx"]