git.iko.soy/typeable/pandoc
on master help sign in
App: extract output settings into module
418bd42Albert Krewinkel
pandoc.cabal+1
@@ -515,6 +515,7 @@ library
515 Text.Pandoc.BCP47,
515 Text.Pandoc.BCP47,
516 Text.Pandoc.Class
516 Text.Pandoc.Class
517 other-modules: Text.Pandoc.App.CommandLineOptions,
517 other-modules: Text.Pandoc.App.CommandLineOptions,
518+ Text.Pandoc.App.OutputSettings,
518 Text.Pandoc.Filter.JSON,
519 Text.Pandoc.Filter.JSON,
519 Text.Pandoc.Filter.Lua,
520 Text.Pandoc.Filter.Lua,
520 Text.Pandoc.Filter.Path,
521 Text.Pandoc.Filter.Path,
src/Text/Pandoc/App.hs+28−201
@@ -1,7 +1,6 @@
1{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE TupleSections #-}
5{-
4{-
6Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu>
5Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu>
7
6
@@ -44,12 +43,10 @@ module Text.Pandoc.App (
44import Prelude
43import Prelude
45import qualified Control.Exception as E
44import qualified Control.Exception as E
46import Control.Monad
45import Control.Monad
47−import Control.Monad.Except (catchError, throwError)
48import Control.Monad.Trans
46import Control.Monad.Trans
49import qualified Data.ByteString as BS
47import qualified Data.ByteString as BS
50import qualified Data.ByteString.Lazy as B
48import qualified Data.ByteString.Lazy as B
51import Data.Char (toLower)
49import Data.Char (toLower)
52−import Data.List (find, isPrefixOf, isSuffixOf)
53import Data.Maybe (fromMaybe, isJust, isNothing)
50import Data.Maybe (fromMaybe, isJust, isNothing)
54import qualified Data.Set as Set
51import qualified Data.Set as Set
55import Data.Text (Text)
52import Data.Text (Text)
@@ -59,8 +56,6 @@ import qualified Data.Text.Lazy.Encoding as TE
59import qualified Data.Text.Encoding.Error as TE
56import qualified Data.Text.Encoding.Error as TE
60import qualified Data.YAML as YAML
57import qualified Data.YAML as YAML
61import Network.URI (URI (..), parseURI)
58import Network.URI (URI (..), parseURI)
62−import Skylighting (defaultSyntaxMap)
63−import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition)
64import System.Directory (getAppUserDataDirectory)
59import System.Directory (getAppUserDataDirectory)
65import System.Exit (exitSuccess)
60import System.Exit (exitSuccess)
66import System.FilePath
61import System.FilePath
@@ -68,7 +63,8 @@ import System.IO (nativeNewline, stdout)
68import qualified System.IO as IO (Newline (..))
63import qualified System.IO as IO (Newline (..))
69import Text.Pandoc
64import Text.Pandoc
70import Text.Pandoc.App.CommandLineOptions (Opt (..), LineEnding (..),
65import Text.Pandoc.App.CommandLineOptions (Opt (..), LineEnding (..),
71− defaultOpts, engines, parseOptions, options)
66+ defaultOpts, parseOptions, options)
67+import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings)
72import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
68import Text.Pandoc.BCP47 (Lang (..), parseBCP47)
73import Text.Pandoc.Builder (setMeta, deleteMeta)
69import Text.Pandoc.Builder (setMeta, deleteMeta)
74import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters)
70import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters)
@@ -83,41 +79,6 @@ import System.Posix.IO (stdOutput)
83import System.Posix.Terminal (queryTerminal)
79import System.Posix.Terminal (queryTerminal)
84#endif
80#endif
85
81
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
121
82
122convertWithOpts :: Opt -> IO ()
83convertWithOpts :: Opt -> IO ()
123convertWithOpts opts = do
84convertWithOpts opts = do
@@ -130,10 +91,6 @@ convertWithOpts opts = do
130 mapM_ (UTF8.hPutStrLn stdout) (optInputFiles opts)
91 mapM_ (UTF8.hPutStrLn stdout) (optInputFiles opts)
131 exitSuccess
92 exitSuccess
132
93
133− epubMetadata <- case optEpubMetadata opts of
134− Nothing -> return Nothing
135− Just fp -> Just <$> UTF8.readFile fp
136−
137 let isPandocCiteproc (JSONFilter f) = takeBaseName f == "pandoc-citeproc"
94 let isPandocCiteproc (JSONFilter f) = takeBaseName f == "pandoc-citeproc"
138 isPandocCiteproc _ = False
95 isPandocCiteproc _ = False
139 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
96 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
@@ -165,30 +122,6 @@ convertWithOpts opts = do
165
122
166 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
123 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
167
124
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−
192 -- TODO: we have to get the input and the output into the state for
125 -- TODO: we have to get the input and the output into the state for
193 -- the sake of the text2tags reader.
126 -- the sake of the text2tags reader.
194 (reader, readerExts) <-
127 (reader, readerExts) <-
@@ -202,34 +135,6 @@ convertWithOpts opts = do
202 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
135 "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
203 _ -> e
136 _ -> e
204
137
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−
233 let convertTabs = tabFilter (if optPreserveTabs opts ||
138 let convertTabs = tabFilter (if optPreserveTabs opts ||
234 readerName == "t2t" ||
139 readerName == "t2t" ||
235 readerName == "man"
140 readerName == "man"
@@ -261,80 +166,41 @@ convertWithOpts opts = do
261 LF -> IO.LF
166 LF -> IO.LF
262 Native -> nativeNewline
167 Native -> nativeNewline
263
168
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−
274 runIO' $ do
169 runIO' $ do
275 setUserDataDir datadir
170 setUserDataDir datadir
276 setInputFiles (optInputFiles opts)
171 setInputFiles (optInputFiles opts)
277 setOutputFile (optOutputFile opts)
172 setOutputFile (optOutputFile opts)
278
173
279− variables <-
174+ outputSettings <- optToOutputSettings opts
280− withList (addStringAsVariable "sourcefile")
175+ let format = outputFormat outputSettings
281− (reverse $ optInputFiles opts)
176+ let writer = outputWriter outputSettings
282− (("outputfile", fromMaybe "-" (optOutputFile opts))
177+ let writerName = outputWriterName outputSettings
283− : optVariables opts)
178+ let writerOptions = outputWriterOptions outputSettings
284− -- we reverse this list because, unlike
179+
285− -- the other option lists here, it is
180+ let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput
286− -- not reversed when parsed from CLI arguments.
181+
287− -- See withList, above.
182+ -- We don't want to send output to the terminal if the user
288− >>=
183+ -- does 'pandoc -t docx input.txt'; though we allow them to
289− withList (addContentsAsVariable "include-before")
184+ -- force this with '-o -'. On posix systems, we detect
290− (optIncludeBeforeBody opts)
185+ -- when stdout is being piped and allow output to stdout
291− >>=
186+ -- in that case, but on Windows we can't.
292− withList (addContentsAsVariable "include-after")
187+#ifdef _WINDOWS
293− (optIncludeAfterBody opts)
188+ let istty = True
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)
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+
316
198
317 abbrevs <- Set.fromList . filter (not . null) . lines <$>
199 abbrevs <- Set.fromList . filter (not . null) . lines <$>
318 case optAbbreviations opts of
200 case optAbbreviations opts of
319 Nothing -> UTF8.toString <$> readDataFile "abbreviations"
201 Nothing -> UTF8.toString <$> readDataFile "abbreviations"
320 Just f -> UTF8.toString <$> readFileStrict f
202 Just f -> UTF8.toString <$> readFileStrict f
321
203
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−
338 metadata <- if format == "jats" &&
204 metadata <- if format == "jats" &&
339 isNothing (lookup "csl" (optMetadata opts)) &&
205 isNothing (lookup "csl" (optMetadata opts)) &&
340 isNothing (lookup "citation-style" (optMetadata opts))
206 isNothing (lookup "citation-style" (optMetadata opts))
@@ -355,41 +221,6 @@ convertWithOpts opts = do
355 Right l' -> setTranslations l'
221 Right l' -> setTranslations l'
356 Nothing -> setTranslations $ Lang "en" "" "US" []
222 Nothing -> setTranslations $ Lang "en" "" "US" []
357
223
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−
393 let readerOpts = def{
224 let readerOpts = def{
394 readerStandalone = standalone
225 readerStandalone = standalone
395 , readerColumns = optColumns opts
226 , readerColumns = optColumns opts
@@ -412,7 +243,7 @@ convertWithOpts opts = do
412 (if extensionEnabled Ext_east_asian_line_breaks
243 (if extensionEnabled Ext_east_asian_line_breaks
413 readerExts &&
244 readerExts &&
414 not (extensionEnabled Ext_east_asian_line_breaks
245 not (extensionEnabled Ext_east_asian_line_breaks
415− writerExts &&
246+ (writerExtensions writerOptions) &&
416 writerWrapText writerOptions == WrapPreserve)
247 writerWrapText writerOptions == WrapPreserve)
417 then (eastAsianLineBreakFilter :)
248 then (eastAsianLineBreakFilter :)
418 else id) $
249 else id) $
@@ -450,7 +281,7 @@ convertWithOpts opts = do
450
281
451 case writer of
282 case writer of
452 ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile
283 ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile
453− TextWriter f -> case maybePdfProg of
284+ TextWriter f -> case outputPdfProgram outputSettings of
454 Just pdfProg -> do
285 Just pdfProg -> do
455 res <- makePDF pdfProg (optPdfEngineArgs opts) f
286 res <- makePDF pdfProg (optPdfEngineArgs opts) f
456 writerOptions doc
287 writerOptions doc
@@ -594,7 +425,3 @@ writerFn :: MonadIO m => IO.Newline -> FilePath -> Text -> m ()
594-- TODO this implementation isn't maximally efficient:
425-- TODO this implementation isn't maximally efficient:
595writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack
426writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack
596writerFn eol f = liftIO . UTF8.writeFileWith eol f . T.unpack
427writerFn 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
268 , optStripComments = False
268 , optStripComments = False
269 }
269 }
270
270
271−lookupHighlightStyle :: Maybe String -> IO (Maybe Style)
271+lookupHighlightStyle :: String -> IO (Maybe Style)
272−lookupHighlightStyle Nothing = return Nothing
272+lookupHighlightStyle s
273−lookupHighlightStyle (Just s)
274 | takeExtension s == ".theme" = -- attempt to load KDE theme
273 | takeExtension s == ".theme" = -- attempt to load KDE theme
275 do contents <- B.readFile s
274 do contents <- B.readFile s
276 case parseTheme contents of
275 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"]