{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE CPP                 #-}
{-# LANGUAGE ScopedTypeVariables #-}
{- |
   Module      : Text.Pandoc.App
   Copyright   : Copyright (C) 2006-2024 John MacFarlane
   License     : GNU GPL, version 2 or above

   Maintainer  : John MacFarlane <jgm@berkeley@edu>
   Stability   : alpha
   Portability : portable

Does a pandoc conversion based on command-line options.
-}
module Text.Pandoc.App (
            convertWithOpts
          , handleOptInfo
          , Opt(..)
          , OptInfo(..)
          , LineEnding(..)
          , IpynbOutput (..)
          , Filter(..)
          , defaultOpts
          , parseOptions
          , parseOptionsFromArgs
          , options
          , applyFilters
          , versionInfo
          ) where
import qualified Control.Exception as E
import Control.Monad ( (>=>), when, forM, forM_ )
import Control.Monad.Trans ( MonadIO(..) )
import Control.Monad.Catch ( MonadMask )
import Control.Monad.Except (throwError)
import qualified Data.ByteString.Lazy as BL
import Data.Maybe (fromMaybe, isJust, isNothing)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TE
import qualified Data.Text.Encoding.Error as TE
import Data.Char (toLower)
import System.Directory (doesDirectoryExist, createDirectory,
                         createDirectoryIfMissing)
import Codec.Archive.Zip (toArchiveOrFail,
                          extractFilesFromArchive, ZipOption(..))
import System.Exit (exitSuccess)
import System.FilePath ( takeBaseName, takeExtension, takeDirectory)
import System.IO (nativeNewline, stdout)
import qualified System.IO as IO (Newline (..))
import Text.Pandoc
import Text.Pandoc.Builder (setMeta)
import Text.Pandoc.MediaBag (mediaItems)
import Text.Pandoc.Image (svgToPng)
import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts,
                            IpynbOutput (..), OptInfo(..))
import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs,
                                           options, handleOptInfo, versionInfo)
import Text.Pandoc.App.Input (InputParameters (..), readInput)
import Text.Pandoc.App.OutputSettings (OutputSettings (..), optToOutputSettings,
                                       sandbox')
import Text.Pandoc.Transforms (applyTransforms, filterIpynbOutput,
                               headerShift, eastAsianLineBreakFilter)
import Text.Collate.Lang (Lang (..), parseLang)
import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), Environment (..),
                           applyFilters)
import qualified Text.Pandoc.Format as Format
import Text.Pandoc.PDF (makePDF)
import Text.Pandoc.Scripting (ScriptingEngine (..), CustomComponents(..))
import Text.Pandoc.SelfContained (makeSelfContained)
import Text.Pandoc.Shared (tshow)
import Text.Pandoc.Writers.Shared (lookupMetaString)
import Text.Pandoc.Readers.Markdown (yamlToMeta)
import qualified Text.Pandoc.UTF8 as UTF8
#ifndef _WINDOWS
import System.Posix.IO (stdOutput)
import System.Posix.Terminal (queryTerminal)
#endif

convertWithOpts :: ScriptingEngine -> Opt -> IO ()
convertWithOpts :: ScriptingEngine -> Opt -> IO ()
convertWithOpts ScriptingEngine
scriptingEngine Opt
opts = do
  let outputFile :: [Char]
outputFile = [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
"-" (Opt -> Maybe [Char]
optOutputFile Opt
opts)
  datadir <- case Opt -> Maybe [Char]
optDataDir Opt
opts of
                  Maybe [Char]
Nothing   -> do
                    d <- IO [Char]
defaultUserDataDir
                    exists <- doesDirectoryExist d
                    return $ if exists
                                then Just d
                                else Nothing
                  Maybe [Char]
mdatadir  -> Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe [Char]
mdatadir

  when (optDumpArgs opts) $
    do UTF8.hPutStrLn stdout (T.pack outputFile)
       mapM_ (UTF8.hPutStrLn stdout . T.pack)
             (fromMaybe ["-"] $ optInputFiles opts)
       exitSuccess

#ifdef _WINDOWS
  let istty = True
#else
  istty <- liftIO $ queryTerminal stdOutput
#endif

  res <- runIO $ convertWithOpts' scriptingEngine istty datadir opts
  case res of
    Left PandocError
e -> PandocError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
E.throwIO PandocError
e
    Right (PandocOutput
output, [LogMessage]
reports) -> do
      case Opt -> Maybe [Char]
optLogFile Opt
opts of
           Maybe [Char]
Nothing      -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
           Just [Char]
logfile -> [Char] -> ByteString -> IO ()
BL.writeFile [Char]
logfile ([LogMessage] -> ByteString
encodeLogMessages [LogMessage]
reports)
      let isWarning :: LogMessage -> Bool
isWarning LogMessage
msg = LogMessage -> Verbosity
messageVerbosity LogMessage
msg Verbosity -> Verbosity -> Bool
forall a. Eq a => a -> a -> Bool
== Verbosity
WARNING
      Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Opt -> Bool
optFailIfWarnings Opt
opts Bool -> Bool -> Bool
&& (LogMessage -> Bool) -> [LogMessage] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any LogMessage -> Bool
isWarning [LogMessage]
reports) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
          PandocError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
E.throwIO PandocError
PandocFailOnWarningError
      let eol :: Newline
eol = case Opt -> LineEnding
optEol Opt
opts of
                     LineEnding
CRLF   -> Newline
IO.CRLF
                     LineEnding
LF     -> Newline
IO.LF
                     LineEnding
Native -> Newline
nativeNewline
      let outputFileDir :: [Char]
outputFileDir = [Char] -> [Char]
takeDirectory [Char]
outputFile
      Bool -> [Char] -> IO ()
createDirectoryIfMissing Bool
True [Char]
outputFileDir
      case PandocOutput
output of
        TextOutput Text
t    -> Newline -> [Char] -> Text -> IO ()
writerFn Newline
eol [Char]
outputFile Text
t
        BinaryOutput ByteString
bs -> [Char] -> ByteString -> IO ()
writeFnBinary [Char]
outputFile ByteString
bs
        ZipOutput ByteString
bs
          | [Char] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Char] -> [Char]
takeExtension [Char]
outputFile)
          , [Char]
outputFile [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
/= [Char]
"-" -> do
             -- create directory and unzip
             [Char] -> IO ()
createDirectory [Char]
outputFile -- will fail if directory exists
             let zipopts :: [ZipOption]
zipopts = [ZipOption
OptRecursive, [Char] -> ZipOption
OptDestination [Char]
outputFile] [ZipOption] -> [ZipOption] -> [ZipOption]
forall a. [a] -> [a] -> [a]
++
                           [ZipOption
OptVerbose | Opt -> Verbosity
optVerbosity Opt
opts Verbosity -> Verbosity -> Bool
forall a. Eq a => a -> a -> Bool
== Verbosity
INFO]
             case ByteString -> Either [Char] Archive
toArchiveOrFail ByteString
bs of
               Right Archive
archive -> [ZipOption] -> Archive -> IO ()
extractFilesFromArchive [ZipOption]
zipopts Archive
archive
               Left [Char]
e -> PandocError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
E.throwIO (PandocError -> IO ()) -> PandocError -> IO ()
forall a b. (a -> b) -> a -> b
$ Text -> PandocError
PandocShouldNeverHappenError (Text -> PandocError) -> Text -> PandocError
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
T.pack [Char]
e
          | Bool
otherwise -> [Char] -> ByteString -> IO ()
writeFnBinary [Char]
outputFile ByteString
bs

convertWithOpts' :: (PandocMonad m, MonadIO m, MonadMask m)
                 => ScriptingEngine
                 -> Bool
                 -> Maybe FilePath
                 -> Opt
                 -> m (PandocOutput, [LogMessage])
convertWithOpts' :: forall (m :: * -> *).
(PandocMonad m, MonadIO m, MonadMask m) =>
ScriptingEngine
-> Bool -> Maybe [Char] -> Opt -> m (PandocOutput, [LogMessage])
convertWithOpts' ScriptingEngine
scriptingEngine Bool
istty Maybe [Char]
datadir Opt
opts = do
  Maybe [Char] -> Opt -> m ()
forall (m :: * -> *). PandocMonad m => Maybe [Char] -> Opt -> m ()
configureCommonState Maybe [Char]
datadir Opt
opts
  let outputFile :: [Char]
outputFile = [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
"-" (Opt -> Maybe [Char]
optOutputFile Opt
opts)
  let filters :: [Filter]
filters = Opt -> [Filter]
optFilters Opt
opts
  let sources :: [[Char]]
sources = case Opt -> Maybe [[Char]]
optInputFiles Opt
opts of
                     Just [[Char]]
xs | Bool -> Bool
not (Opt -> Bool
optIgnoreArgs Opt
opts) -> [[Char]]
xs
                     Maybe [[Char]]
_ -> [[Char]
"-"]

  let defFlavor :: Text -> FlavoredFormat
defFlavor Text
fmt = Text -> ExtensionsDiff -> FlavoredFormat
Format.FlavoredFormat Text
fmt ExtensionsDiff
forall a. Monoid a => a
mempty
  -- assign reader and writer based on options and filenames
  flvrd@(Format.FlavoredFormat readerNameBase _extsDiff) <-
    case Opt -> Maybe Text
optFrom Opt
opts of
      Just Text
f  -> Text -> m FlavoredFormat
forall (m :: * -> *). PandocMonad m => Text -> m FlavoredFormat
Format.parseFlavoredFormat Text
f
      Maybe Text
Nothing -> case [[Char]] -> Maybe FlavoredFormat
Format.formatFromFilePaths [[Char]]
sources of
        Just FlavoredFormat
f' -> FlavoredFormat -> m FlavoredFormat
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return FlavoredFormat
f'
        Maybe FlavoredFormat
Nothing | [[Char]]
sources [[Char]] -> [[Char]] -> Bool
forall a. Eq a => a -> a -> Bool
== [[Char]
"-"] -> FlavoredFormat -> m FlavoredFormat
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (FlavoredFormat -> m FlavoredFormat)
-> FlavoredFormat -> m FlavoredFormat
forall a b. (a -> b) -> a -> b
$ Text -> FlavoredFormat
defFlavor Text
"markdown"
                | Bool
otherwise -> do
                    LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text -> LogMessage
CouldNotDeduceFormat
                      (([Char] -> Text) -> [[Char]] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map ([Char] -> Text
T.pack ([Char] -> Text) -> ([Char] -> [Char]) -> [Char] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [Char]
takeExtension) [[Char]]
sources) Text
"markdown"
                    FlavoredFormat -> m FlavoredFormat
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (FlavoredFormat -> m FlavoredFormat)
-> FlavoredFormat -> m FlavoredFormat
forall a b. (a -> b) -> a -> b
$ Text -> FlavoredFormat
defFlavor Text
"markdown"

  let makeSandboxed Reader PandocPure
pureReader =
       case Reader PandocPure
pureReader of
            TextReader forall a. ToSources a => ReaderOptions -> a -> PandocPure Pandoc
r
              -> (forall a. ToSources a => ReaderOptions -> a -> m Pandoc)
-> Reader m
forall (m :: * -> *).
(forall a. ToSources a => ReaderOptions -> a -> m Pandoc)
-> Reader m
TextReader ((forall a. ToSources a => ReaderOptions -> a -> m Pandoc)
 -> Reader m)
-> (forall a. ToSources a => ReaderOptions -> a -> m Pandoc)
-> Reader m
forall a b. (a -> b) -> a -> b
$ \ReaderOptions
o a
t -> Opt -> PandocPure Pandoc -> m Pandoc
forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
Opt -> PandocPure a -> m a
sandbox' Opt
opts (ReaderOptions -> a -> PandocPure Pandoc
forall a. ToSources a => ReaderOptions -> a -> PandocPure Pandoc
r ReaderOptions
o a
t)
            ByteStringReader ReaderOptions -> ByteString -> PandocPure Pandoc
r
              -> (ReaderOptions -> ByteString -> m Pandoc) -> Reader m
forall (m :: * -> *).
(ReaderOptions -> ByteString -> m Pandoc) -> Reader m
ByteStringReader ((ReaderOptions -> ByteString -> m Pandoc) -> Reader m)
-> (ReaderOptions -> ByteString -> m Pandoc) -> Reader m
forall a b. (a -> b) -> a -> b
$ \ReaderOptions
o ByteString
t -> Opt -> PandocPure Pandoc -> m Pandoc
forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
Opt -> PandocPure a -> m a
sandbox' Opt
opts (ReaderOptions -> ByteString -> PandocPure Pandoc
r ReaderOptions
o ByteString
t)

  (reader, readerExts) <-
    if ".lua" `T.isSuffixOf` readerNameBase
       then do
            let scriptPath = Text -> [Char]
T.unpack Text
readerNameBase
            components <- engineLoadCustom scriptingEngine scriptPath
            r <- case customReader components of
                   Maybe (Reader m)
Nothing -> PandocError -> m (Reader m)
forall a. PandocError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (PandocError -> m (Reader m)) -> PandocError -> m (Reader m)
forall a b. (a -> b) -> a -> b
$ Text -> PandocError
PandocAppError (Text -> PandocError) -> Text -> PandocError
forall a b. (a -> b) -> a -> b
$
                               Text
readerNameBase Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" does not contain a custom reader"
                   Just Reader m
r -> Reader m -> m (Reader m)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Reader m
r
            let extsConf = ExtensionsConfig -> Maybe ExtensionsConfig -> ExtensionsConfig
forall a. a -> Maybe a -> a
fromMaybe ExtensionsConfig
forall a. Monoid a => a
mempty (CustomComponents m -> Maybe ExtensionsConfig
forall (m :: * -> *). CustomComponents m -> Maybe ExtensionsConfig
customExtensions CustomComponents m
components)
            rexts <- Format.applyExtensionsDiff extsConf flvrd
            return (r, rexts)
       else if optSandbox opts
               then case runPure (getReader flvrd) of
                      Left PandocError
e -> PandocError -> m (Reader m, Extensions)
forall a. PandocError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError PandocError
e
                      Right (Reader PandocPure
r, Extensions
rexts) -> (Reader m, Extensions) -> m (Reader m, Extensions)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Reader PandocPure -> Reader m
forall {m :: * -> *}.
(PandocMonad m, MonadIO m) =>
Reader PandocPure -> Reader m
makeSandboxed Reader PandocPure
r, Extensions
rexts)
               else getReader flvrd

  outputSettings <- optToOutputSettings scriptingEngine opts
  let format = OutputSettings m -> Text
forall (m :: * -> *). OutputSettings m -> Text
outputFormat OutputSettings m
outputSettings
  let writer = OutputSettings m -> Writer m
forall (m :: * -> *). OutputSettings m -> Writer m
outputWriter OutputSettings m
outputSettings
  let writerOptions = OutputSettings m -> WriterOptions
forall (m :: * -> *). OutputSettings m -> WriterOptions
outputWriterOptions OutputSettings m
outputSettings

  -- whether we are targeting PDF.
  let pdfOutput = (Char -> Char) -> [Char] -> [Char]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower ([Char] -> [Char]
takeExtension [Char]
outputFile) [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char]
".pdf" Bool -> Bool -> Bool
||
                  Opt -> Maybe Text
optTo Opt
opts Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"pdf"
  -- whether standalone output should be produced.
  let bibOutput = Text
format Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"bibtex", Text
"biblatex", Text
"csljson"]
  let standalone = Maybe (Template Text) -> Bool
forall a. Maybe a -> Bool
isJust (WriterOptions -> Maybe (Template Text)
writerTemplate WriterOptions
writerOptions) Bool -> Bool -> Bool
|| Bool
bibOutput

  --
  -- Sanity checks
  --
  when (pdfOutput && readerNameBase == "latex") $
    case optInputFiles opts of
      Just ([Char]
inputFile:[[Char]]
_) -> LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> LogMessage
UnusualConversion (Text -> LogMessage) -> Text -> LogMessage
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
T.pack ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$
        [Char]
"to convert a .tex file to PDF, you get better results by using pdflatex "
          [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"(or lualatex or xelatex) directly, try `pdflatex " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
inputFile
          [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` instead of `pandoc " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
inputFile [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
" -o " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
outputFile [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"`."
      Maybe [[Char]]
_ -> () -> m ()
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

  -- We don't want to send output to the terminal if the user
  -- does 'pandoc -t docx input.txt'; though we allow them to
  -- force this with '-o -'.  On posix systems, we detect
  -- when stdout is being piped and allow output to stdout
  -- in that case, but on Windows we can't.
  when ((pdfOutput || not (isTextFormat format)) &&
           istty && isNothing ( optOutputFile opts)) $
    throwError $ PandocAppError $
            "Cannot write " <> (if pdfOutput then "pdf" else format) <>
            " output to terminal.\n" <>
            "Specify an output file using the -o option, or " <>
            "use '-o -' to force output to stdout."

  when (readerNameBase == "markdown_github" ||
        format == "markdown_github") $
    report $ Deprecated "markdown_github" "Use gfm instead."

  abbrevs <- readAbbreviations (optAbbreviations opts)
  let readerOpts = ReaderOptions
forall a. Default a => a
def{
          readerStandalone = standalone
        , readerColumns = optColumns opts
        , readerTabStop = optTabStop opts
        , readerIndentedCodeClasses = optIndentedCodeClasses opts
        , readerDefaultImageExtension = optDefaultImageExtension opts
        , readerTrackChanges = optTrackChanges opts
        , readerAbbreviations = abbrevs
        , readerExtensions = readerExts
        , readerStripComments = optStripComments opts
        }

  metadataFromFile <- getMetadataFromFiles readerNameBase readerOpts
                         (optMetadataFiles opts)

  let transforms = (case Opt -> Int
optShiftHeadingLevelBy Opt
opts of
                        Int
0             -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> a
id
                        Int
x             -> (Int -> Pandoc -> Pandoc
headerShift Int
x (Pandoc -> Pandoc) -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> [a] -> [a]
:)) ([Pandoc -> Pandoc] -> [Pandoc -> Pandoc])
-> ([Pandoc -> Pandoc] -> [Pandoc -> Pandoc])
-> [Pandoc -> Pandoc]
-> [Pandoc -> Pandoc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                   (if Extension -> Extensions -> Bool
extensionEnabled Extension
Ext_east_asian_line_breaks
                          Extensions
readerExts Bool -> Bool -> Bool
&&
                       Bool -> Bool
not (Extension -> Extensions -> Bool
extensionEnabled Extension
Ext_east_asian_line_breaks
                            (WriterOptions -> Extensions
writerExtensions WriterOptions
writerOptions) Bool -> Bool -> Bool
&&
                            WriterOptions -> WrapOption
writerWrapText WriterOptions
writerOptions WrapOption -> WrapOption -> Bool
forall a. Eq a => a -> a -> Bool
== WrapOption
WrapPreserve)
                       then (Pandoc -> Pandoc
eastAsianLineBreakFilter (Pandoc -> Pandoc) -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> [a] -> [a]
:)
                       else [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> a
id) ([Pandoc -> Pandoc] -> [Pandoc -> Pandoc])
-> ([Pandoc -> Pandoc] -> [Pandoc -> Pandoc])
-> [Pandoc -> Pandoc]
-> [Pandoc -> Pandoc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                   (case Opt -> IpynbOutput
optIpynbOutput Opt
opts of
                     IpynbOutput
_ | Text
readerNameBase Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"ipynb" -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> a
id
                     IpynbOutput
IpynbOutputAll  -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> a
id
                     IpynbOutput
IpynbOutputNone -> (Maybe Format -> Pandoc -> Pandoc
filterIpynbOutput Maybe Format
forall a. Maybe a
Nothing (Pandoc -> Pandoc) -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> [a] -> [a]
:)
                     IpynbOutput
IpynbOutputBest -> (Maybe Format -> Pandoc -> Pandoc
filterIpynbOutput (Format -> Maybe Format
forall a. a -> Maybe a
Just (Format -> Maybe Format) -> Format -> Maybe Format
forall a b. (a -> b) -> a -> b
$
                                   if Text -> Bool
htmlFormat Text
format
                                      then Text -> Format
Format Text
"html"
                                      else
                                        case Text
format of
                                          Text
"latex"  -> Text -> Format
Format Text
"latex"
                                          Text
"beamer" -> Text -> Format
Format Text
"latex"
                                          Text
_        -> Text -> Format
Format Text
format) (Pandoc -> Pandoc) -> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a. a -> [a] -> [a]
:))
                   ([Pandoc -> Pandoc] -> [Pandoc -> Pandoc])
-> [Pandoc -> Pandoc] -> [Pandoc -> Pandoc]
forall a b. (a -> b) -> a -> b
$ []

  let isPandocCiteproc (JSONFilter [Char]
f) = [Char] -> [Char]
takeBaseName [Char]
f [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char]
"pandoc-citeproc"
      isPandocCiteproc Filter
_              = Bool
False

  when (any isPandocCiteproc filters) $
    report $ Deprecated "pandoc-citeproc filter"
             "Use --citeproc instead."

  let cslMetadata =
        (Meta -> Meta)
-> ([Char] -> Meta -> Meta) -> Maybe [Char] -> Meta -> Meta
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Meta -> Meta
forall a. a -> a
id (Text -> [Char] -> Meta -> Meta
forall a b. (HasMeta a, ToMetaValue b) => Text -> b -> a -> a
forall b. ToMetaValue b => Text -> b -> Meta -> Meta
setMeta Text
"csl") (Opt -> Maybe [Char]
optCSL Opt
opts) (Meta -> Meta) -> (Meta -> Meta) -> Meta -> Meta
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        (case Opt -> [[Char]]
optBibliography Opt
opts of
           [] -> Meta -> Meta
forall a. a -> a
id
           [[Char]]
xs -> Text -> [[Char]] -> Meta -> Meta
forall a b. (HasMeta a, ToMetaValue b) => Text -> b -> a -> a
forall b. ToMetaValue b => Text -> b -> Meta -> Meta
setMeta Text
"bibliography" [[Char]]
xs) (Meta -> Meta) -> (Meta -> Meta) -> Meta -> Meta
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
        (Meta -> Meta)
-> ([Char] -> Meta -> Meta) -> Maybe [Char] -> Meta -> Meta
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Meta -> Meta
forall a. a -> a
id (Text -> [Char] -> Meta -> Meta
forall a b. (HasMeta a, ToMetaValue b) => Text -> b -> a -> a
forall b. ToMetaValue b => Text -> b -> Meta -> Meta
setMeta Text
"citation-abbreviations")
                       (Opt -> Maybe [Char]
optCitationAbbreviations Opt
opts) (Meta -> Meta) -> Meta -> Meta
forall a b. (a -> b) -> a -> b
$ Meta
forall a. Monoid a => a
mempty

  let filterEnv = ReaderOptions -> WriterOptions -> Environment
Environment ReaderOptions
readerOpts WriterOptions
writerOptions

  let inputParams = InputParameters
        { inputReader :: Reader m
inputReader = Reader m
reader
        , inputReaderName :: Text
inputReaderName = Text
readerNameBase
        , inputReaderOptions :: ReaderOptions
inputReaderOptions = ReaderOptions
readerOpts
        , inputSources :: [[Char]]
inputSources = [[Char]]
sources
        , inputFileScope :: Bool
inputFileScope = Opt -> Bool
optFileScope Opt
opts
        , inputSpacesPerTab :: Maybe Int
inputSpacesPerTab = if Opt -> Bool
optPreserveTabs Opt
opts
                              then Maybe Int
forall a. Maybe a
Nothing
                              else Int -> Maybe Int
forall a. a -> Maybe a
Just (Opt -> Int
optTabStop Opt
opts)
        }

  doc <- readInput inputParams
          >>= ( return . adjustMetadata (metadataFromFile <>)
            >=> return . adjustMetadata (<> optMetadata opts)
            >=> return . adjustMetadata (<> cslMetadata)
            >=> applyFilters scriptingEngine filterEnv filters [T.unpack format]
            >=> applyTransforms transforms
            >=> (if not (optSandbox opts) &&
                    (isJust (optExtractMedia opts)
                     || format == "docx") -- for fallback pngs
                 then fillMediaBag
                 else return)
            >=> maybe return extractMedia (optExtractMedia opts)
            )

  when (format == "docx" && not (optSandbox opts)) $ do
    createPngFallbacks (writerDpi writerOptions)

  output <- case writer of
    ByteStringWriter WriterOptions -> Pandoc -> m ByteString
f
      | Text
format Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"chunkedhtml" -> ByteString -> PandocOutput
ZipOutput (ByteString -> PandocOutput) -> m ByteString -> m PandocOutput
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterOptions -> Pandoc -> m ByteString
f WriterOptions
writerOptions Pandoc
doc
      | Bool
otherwise -> ByteString -> PandocOutput
BinaryOutput (ByteString -> PandocOutput) -> m ByteString -> m PandocOutput
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterOptions -> Pandoc -> m ByteString
f WriterOptions
writerOptions Pandoc
doc
    TextWriter WriterOptions -> Pandoc -> m Text
f -> case OutputSettings m -> Maybe [Char]
forall (m :: * -> *). OutputSettings m -> Maybe [Char]
outputPdfProgram OutputSettings m
outputSettings of
      Just [Char]
pdfProg | Bool
pdfOutput -> do
              res <- [Char]
-> [[Char]]
-> (WriterOptions -> Pandoc -> m Text)
-> WriterOptions
-> Pandoc
-> m (Either ByteString ByteString)
forall (m :: * -> *).
(PandocMonad m, MonadIO m, MonadMask m) =>
[Char]
-> [[Char]]
-> (WriterOptions -> Pandoc -> m Text)
-> WriterOptions
-> Pandoc
-> m (Either ByteString ByteString)
makePDF [Char]
pdfProg (Opt -> [[Char]]
optPdfEngineOpts Opt
opts) WriterOptions -> Pandoc -> m Text
f
                      WriterOptions
writerOptions Pandoc
doc
              case res of
                   Right ByteString
pdf -> PandocOutput -> m PandocOutput
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (PandocOutput -> m PandocOutput) -> PandocOutput -> m PandocOutput
forall a b. (a -> b) -> a -> b
$ ByteString -> PandocOutput
BinaryOutput ByteString
pdf
                   Left ByteString
err' -> PandocError -> m PandocOutput
forall a. PandocError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (PandocError -> m PandocOutput) -> PandocError -> m PandocOutput
forall a b. (a -> b) -> a -> b
$ Text -> PandocError
PandocPDFError (Text -> PandocError) -> Text -> PandocError
forall a b. (a -> b) -> a -> b
$
                                   LazyText -> Text
TL.toStrict (OnDecodeError -> ByteString -> LazyText
TE.decodeUtf8With OnDecodeError
TE.lenientDecode ByteString
err')

      Maybe [Char]
_ -> do
              let ensureNl :: Text -> Text
ensureNl Text
t
                    | Bool
standalone = Text
t
                    | Text -> Bool
T.null Text
t Bool -> Bool -> Bool
|| HasCallStack => Text -> Char
Text -> Char
T.last Text
t Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n' = Text
t Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Char -> Text
T.singleton Char
'\n'
                    | Bool
otherwise = Text
t
              textOutput <- Text -> Text
ensureNl (Text -> Text) -> m Text -> m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterOptions -> Pandoc -> m Text
f WriterOptions
writerOptions Pandoc
doc
              if htmlFormat format &&
                  (optSelfContained opts || optEmbedResources opts)
                 then if optSandbox opts
                         then sandbox' opts $
                              TextOutput <$> makeSelfContained textOutput
                         else TextOutput <$> makeSelfContained textOutput
                 else return $ TextOutput textOutput
  reports <- getLog
  return (output, reports)

data PandocOutput =
      TextOutput Text
    | BinaryOutput BL.ByteString
    | ZipOutput BL.ByteString
  deriving (Int -> PandocOutput -> [Char] -> [Char]
[PandocOutput] -> [Char] -> [Char]
PandocOutput -> [Char]
(Int -> PandocOutput -> [Char] -> [Char])
-> (PandocOutput -> [Char])
-> ([PandocOutput] -> [Char] -> [Char])
-> Show PandocOutput
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
$cshowsPrec :: Int -> PandocOutput -> [Char] -> [Char]
showsPrec :: Int -> PandocOutput -> [Char] -> [Char]
$cshow :: PandocOutput -> [Char]
show :: PandocOutput -> [Char]
$cshowList :: [PandocOutput] -> [Char] -> [Char]
showList :: [PandocOutput] -> [Char] -> [Char]
Show)

-- | Configure the common state
configureCommonState :: PandocMonad m => Maybe FilePath -> Opt -> m ()
configureCommonState :: forall (m :: * -> *). PandocMonad m => Maybe [Char] -> Opt -> m ()
configureCommonState Maybe [Char]
datadir Opt
opts = do
  Maybe [Char] -> m ()
forall (m :: * -> *). PandocMonad m => Maybe [Char] -> m ()
setUserDataDir Maybe [Char]
datadir
  Bool -> m ()
forall (m :: * -> *). PandocMonad m => Bool -> m ()
setTrace (Opt -> Bool
optTrace Opt
opts)
  Verbosity -> m ()
forall (m :: * -> *). PandocMonad m => Verbosity -> m ()
setVerbosity (Opt -> Verbosity
optVerbosity Opt
opts)
  [[Char]] -> m ()
forall (m :: * -> *). PandocMonad m => [[Char]] -> m ()
setResourcePath (Opt -> [[Char]]
optResourcePath Opt
opts)
  [[Char]] -> m ()
forall (m :: * -> *). PandocMonad m => [[Char]] -> m ()
setInputFiles ([[Char]] -> Maybe [[Char]] -> [[Char]]
forall a. a -> Maybe a -> a
fromMaybe [[Char]
"-"] (Opt -> Maybe [[Char]]
optInputFiles Opt
opts))
  Maybe [Char] -> m ()
forall (m :: * -> *). PandocMonad m => Maybe [Char] -> m ()
setOutputFile (Opt -> Maybe [Char]
optOutputFile Opt
opts)
  Bool -> m ()
forall (m :: * -> *). PandocMonad m => Bool -> m ()
setNoCheckCertificate (Opt -> Bool
optNoCheckCertificate Opt
opts)

  ((Text, Text) -> m ()) -> [(Text, Text)] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ((Text -> Text -> m ()) -> (Text, Text) -> m ()
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Text -> Text -> m ()
forall (m :: * -> *). PandocMonad m => Text -> Text -> m ()
setRequestHeader) (Opt -> [(Text, Text)]
optRequestHeaders Opt
opts)

  case Text -> Meta -> Text
lookupMetaString Text
"lang" (Opt -> Meta
optMetadata Opt
opts) of
    Text
""      -> Lang -> m ()
forall (m :: * -> *). PandocMonad m => Lang -> m ()
setTranslations (Lang -> m ()) -> Lang -> m ()
forall a b. (a -> b) -> a -> b
$ Text
-> Maybe Text
-> Maybe Text
-> [Text]
-> [(Text, [(Text, Text)])]
-> [Text]
-> Lang
Lang Text
"en" Maybe Text
forall a. Maybe a
Nothing (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"US") [] [] []
    Text
l       -> case Text -> Either [Char] Lang
parseLang Text
l of
                 Left [Char]
_   -> LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> LogMessage
InvalidLang Text
l
                 Right Lang
l' -> Lang -> m ()
forall (m :: * -> *). PandocMonad m => Lang -> m ()
setTranslations Lang
l'

-- | Retrieves the set of abbreviations to be used by pandoc. These currently
-- only affect the Markdown reader.
readAbbreviations :: PandocMonad m => Maybe FilePath -> m (Set.Set Text)
readAbbreviations :: forall (m :: * -> *). PandocMonad m => Maybe [Char] -> m (Set Text)
readAbbreviations Maybe [Char]
mbfilepath =
  (case Maybe [Char]
mbfilepath of
    Maybe [Char]
Nothing -> [Char] -> m ByteString
forall (m :: * -> *). PandocMonad m => [Char] -> m ByteString
readDataFile [Char]
"abbreviations"
    Just [Char]
f  -> [Char] -> m ByteString
forall (m :: * -> *). PandocMonad m => [Char] -> m ByteString
readFileStrict [Char]
f)
    m ByteString -> (ByteString -> m (Set Text)) -> m (Set Text)
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Text -> Set Text) -> m Text -> m (Set Text)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Text] -> Set Text
forall a. Ord a => [a] -> Set a
Set.fromList ([Text] -> Set Text) -> (Text -> [Text]) -> Text -> Set Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null) ([Text] -> [Text]) -> (Text -> [Text]) -> Text -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.lines) (m Text -> m (Set Text))
-> (ByteString -> m Text) -> ByteString -> m (Set Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
         [Char] -> ByteString -> m Text
forall (m :: * -> *).
PandocMonad m =>
[Char] -> ByteString -> m Text
toTextM ([Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
forall a. Monoid a => a
mempty Maybe [Char]
mbfilepath)

createPngFallbacks :: (PandocMonad m, MonadIO m) => Int -> m ()
createPngFallbacks :: forall (m :: * -> *). (PandocMonad m, MonadIO m) => Int -> m ()
createPngFallbacks Int
dpi = do
  -- create fallback pngs for svgs
  items <- MediaBag -> [([Char], Text, ByteString)]
mediaItems (MediaBag -> [([Char], Text, ByteString)])
-> m MediaBag -> m [([Char], Text, ByteString)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m MediaBag
forall (m :: * -> *). PandocMonad m => m MediaBag
getMediaBag
  forM_ items $ \([Char]
fp, Text
mt, ByteString
bs) ->
    case (Char -> Bool) -> Text -> Text
T.takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
';') Text
mt of
      Text
"image/svg+xml" -> do
        res <- Int -> ByteString -> m (Either Text ByteString)
forall (m :: * -> *).
MonadIO m =>
Int -> ByteString -> m (Either Text ByteString)
svgToPng Int
dpi ByteString
bs
        case res of
          Right ByteString
bs' -> do
            let fp' :: [Char]
fp' = [Char]
fp [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
".png"
            [Char] -> Maybe Text -> ByteString -> m ()
forall (m :: * -> *).
PandocMonad m =>
[Char] -> Maybe Text -> ByteString -> m ()
insertMedia [Char]
fp' (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"image/png") ByteString
bs'
          Left Text
e -> LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> Text -> LogMessage
CouldNotConvertImage ([Char] -> Text
T.pack [Char]
fp) (Text -> Text
forall a. Show a => a -> Text
tshow Text
e)
      Text
_ -> () -> m ()
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

getMetadataFromFiles :: PandocMonad m
                     => Text -> ReaderOptions -> [FilePath] -> m Meta
getMetadataFromFiles :: forall (m :: * -> *).
PandocMonad m =>
Text -> ReaderOptions -> [[Char]] -> m Meta
getMetadataFromFiles Text
readerFormat ReaderOptions
readerOpts = \case
  []    -> Meta -> m Meta
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Meta
forall a. Monoid a => a
mempty
  [[Char]]
paths -> [Meta] -> Meta
forall a. Monoid a => [a] -> a
mconcat ([Meta] -> Meta) -> m [Meta] -> m Meta
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> do
    -- If format is markdown or commonmark, use the enabled extensions,
    -- otherwise treat metadata as pandoc markdown (see #7926, #6832)
    let readerOptsMeta :: ReaderOptions
readerOptsMeta =
          if Text
readerFormat Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"markdown", Text
"commonmark"]
          then ReaderOptions
readerOpts
          else ReaderOptions
readerOpts{ readerExtensions = pandocExtensions }
    [[Char]] -> ([Char] -> m Meta) -> m [Meta]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [[Char]]
paths (([Char] -> m Meta) -> m [Meta]) -> ([Char] -> m Meta) -> m [Meta]
forall a b. (a -> b) -> a -> b
$ \[Char]
path -> do
      raw <- [Char] -> m ByteString
forall (m :: * -> *). PandocMonad m => [Char] -> m ByteString
readMetadataFile [Char]
path
      yamlToMeta readerOptsMeta (Just path) raw

htmlFormat :: Text -> Bool
htmlFormat :: Text -> Bool
htmlFormat = (Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"html",Text
"html4",Text
"html5",Text
"s5",Text
"slidy",
                      Text
"slideous",Text
"dzslides",Text
"revealjs"])

isTextFormat :: Text -> Bool
isTextFormat :: Text -> Bool
isTextFormat Text
s = Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Text
"odt",Text
"docx",Text
"epub2",Text
"epub3",Text
"epub",Text
"pptx"]

adjustMetadata :: (Meta -> Meta) -> Pandoc -> Pandoc
adjustMetadata :: (Meta -> Meta) -> Pandoc -> Pandoc
adjustMetadata Meta -> Meta
f (Pandoc Meta
meta [Block]
bs) = Meta -> [Block] -> Pandoc
Pandoc (Meta -> Meta
f Meta
meta) [Block]
bs

writeFnBinary :: FilePath -> BL.ByteString -> IO ()
writeFnBinary :: [Char] -> ByteString -> IO ()
writeFnBinary [Char]
"-" = ByteString -> IO ()
BL.putStr
writeFnBinary [Char]
f   = [Char] -> ByteString -> IO ()
BL.writeFile ([Char] -> [Char]
UTF8.encodePath [Char]
f)

writerFn :: IO.Newline -> FilePath -> Text -> IO ()
writerFn :: Newline -> [Char] -> Text -> IO ()
writerFn Newline
eol [Char]
"-" = Newline -> Text -> IO ()
UTF8.putStrWith Newline
eol
writerFn Newline
eol [Char]
f   = Newline -> [Char] -> Text -> IO ()
UTF8.writeFileWith Newline
eol [Char]
f