@@ -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 /= '-')