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

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

    Conversion of LaTeX documents to PDF.
    -}
    module Text.Pandoc.PDF ( makePDF ) where

    import qualified Codec.Picture as JP
    import qualified Control.Exception as E
    import Control.Monad.Trans (MonadIO (..))
    import Control.Monad (foldM_)
    import Crypto.Hash (hashWith, SHA1(SHA1))
    import qualified Data.ByteString as BS
    import Data.ByteString.Lazy (ByteString)
    import qualified Data.ByteString.Lazy as BL
    import qualified Data.ByteString.Lazy.Char8 as BC
    import Data.Maybe (fromMaybe)
    import Data.Text (Text)
    import qualified Data.Text as T
    import qualified Data.Text.Lazy as TL
    import Data.Text.Lazy.Encoding (decodeUtf8')
    import Text.Printf (printf)
    import Data.Char (ord, isAscii, isSpace)
    import System.Directory
    import System.Environment
    import System.Exit (ExitCode (..))
    import System.FilePath
    import System.IO (hClose)
    import System.IO.Temp (withSystemTempDirectory, withTempDirectory,
                           withTempFile)
    import qualified System.IO.Error as IE
    import Text.DocLayout (literal, render, hsep)
    import Text.Pandoc.Definition
    import Text.Pandoc.Error (PandocError (PandocPDFProgramNotFoundError))
    import Text.Pandoc.MIME (getMimeType)
    import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..))
    import Text.Pandoc.Extensions (disableExtension, Extension(Ext_smart))
    import Text.Pandoc.Process (pipeProcess)
    import System.Process (readProcessWithExitCode)
    import Text.Pandoc.Shared (inDirectory, stringify, tshow)
    import qualified Text.Pandoc.UTF8 as UTF8
    import Text.Pandoc.Walk (walkM)
    import Text.Pandoc.Writers.Shared (getField, metaToContext)
    import Control.Monad.Catch (MonadMask)
    #ifdef _WINDOWS
    import Data.List (intercalate)
    #endif
    import Data.List (isPrefixOf, find)
    import Text.Pandoc.Class (fillMediaBag, getVerbosity, setVerbosity,
                              readFileStrict, fileExists,
                              report, extractMedia, PandocMonad, runIOorExplode)
    import Text.Pandoc.Logging
    import Text.DocTemplates ( FromContext(lookupContext) )

    #ifdef _WINDOWS
    changePathSeparators :: FilePath -> FilePath
    changePathSeparators =
      -- We filter out backslashes because an initial `C:\` gets
      -- retained by `splitDirectories`, see #6173:
      intercalate "/" . map (filter (/='\\')) . splitDirectories
    #endif

    makePDF :: (PandocMonad m, MonadIO m, MonadMask m)
            => String              -- ^ pdf creator (pdflatex, lualatex, xelatex,
                                   -- wkhtmltopdf, weasyprint, prince, context,
                                   -- pdfroff, pagedjs,
                                   -- or path to executable)
            -> [String]            -- ^ arguments to pass to pdf creator
            -> (WriterOptions -> Pandoc -> m Text)  -- ^ writer
            -> WriterOptions       -- ^ options
            -> Pandoc              -- ^ document
            -> m (Either ByteString ByteString)
    makePDF :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m, MonadMask m) =>
    String
    -> [String]
    -> (WriterOptions -> Pandoc -> m Text)
    -> WriterOptions
    -> Pandoc
    -> m (Either ByteString ByteString)
    makePDF String
    program [String]
    pdfargs WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc =
      case String -> String
    takeBaseName String
    program of
        String
    "wkhtmltopdf" -> String
    -> [String]
    -> (WriterOptions -> Pandoc -> m Text)
    -> WriterOptions
    -> Pandoc
    -> m (Either ByteString ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> (WriterOptions -> Pandoc -> m Text)
    -> WriterOptions
    -> Pandoc
    -> m (Either ByteString ByteString)
    makeWithWkhtmltopdf String
    program [String]
    pdfargs WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc
        String
    prog | String
    prog String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [String
    "pagedjs-cli" ,String
    "weasyprint", String
    "prince"] -> do
          let mkOutArgs :: a -> [a]
    mkOutArgs a
    f =
                if String
    program String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [String
    "pagedjs-cli", String
    "prince"]
                   then [a
    "-o", a
    f]
                   else [a
    f]
          source <- WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc
          verbosity <- getVerbosity
          liftIO $ toPdfViaTempFile verbosity program pdfargs mkOutArgs ".html" source
        String
    "typst" -> do
          source <- WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc
          verbosity <- getVerbosity
          liftIO $
            toPdfViaTempFile verbosity program ("compile":pdfargs) (:[]) ".typ" source
        String
    "pdfroff" -> do
          source <- WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc
          let paperargs =
                case Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    lookupContext Text
    "papersize" (WriterOptions -> Context Text
    writerVariables WriterOptions
    opts) of
                  Just Text
    s
                    | Int -> Text -> Text
    T.takeEnd Int
    1 Text
    s Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    == Text
    "l" -> [String
    "-P-p" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <>
                                               Text -> String
    T.unpack (Int -> Text -> Text
    T.dropEnd Int
    1 Text
    s), String
    "-P-l"]
                    | Bool
    otherwise -> [String
    "-P-p" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Text -> String
    T.unpack Text
    s]
                  Maybe Text
    Nothing -> []
          let args   = [String
    "-ms", String
    "-mpdfmark", String
    "-mspdf",
                        String
    "-e", String
    "-t", String
    "-k", String
    "-KUTF-8", String
    "-i"] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
                       [String
    "-U" | Text
    ".PDFPIC" Text -> Text -> Bool
    `T.isInfixOf` Text
    source] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
                        [String]
    paperargs [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    pdfargs
          generic2pdf program args source
        String
    "groff" -> do
          source <- WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts Pandoc
    doc
          let paperargs =
                case Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    lookupContext Text
    "papersize" (WriterOptions -> Context Text
    writerVariables WriterOptions
    opts) of
                  Just Text
    s
                    | Int -> Text -> Text
    T.takeEnd Int
    1 Text
    s Text -> Text -> Bool
    forall a. Eq a => a -> a -> Bool
    == Text
    "l" -> [String
    "-P-p" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <>
                                               Text -> String
    T.unpack (Int -> Text -> Text
    T.dropEnd Int
    1 Text
    s), String
    "-P-l"]
                    | Bool
    otherwise -> [String
    "-P-p" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> Text -> String
    T.unpack Text
    s]
                  Maybe Text
    Nothing -> []
          let args   = [String
    "-ms", String
    "-Tpdf",
                        String
    "-e", String
    "-t", String
    "-k", String
    "-KUTF-8"] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
                       [String
    "-U" | Text
    ".PDFPIC" Text -> Text -> Bool
    `T.isInfixOf` Text
    source] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++
                        [String]
    paperargs [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    pdfargs
          generic2pdf program args source
        String
    baseProg -> do
          String
    -> (String -> m (Either ByteString ByteString))
    -> m (Either ByteString ByteString)
    forall (m :: * -> *) a.
    (PandocMonad m, MonadMask m, MonadIO m) =>
    String -> (String -> m a) -> m a
    withTempDir String
    "tex2pdf." ((String -> m (Either ByteString ByteString))
     -> m (Either ByteString ByteString))
    -> (String -> m (Either ByteString ByteString))
    -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ \String
    tmpdir' -> do
    #ifdef _WINDOWS
            -- note:  we want / even on Windows, for TexLive
            let tmpdir = changePathSeparators tmpdir'
    #else
            let tmpdir :: String
    tmpdir = String
    tmpdir'
    #endif
            doc' <- WriterOptions -> String -> Pandoc -> m Pandoc
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    WriterOptions -> String -> Pandoc -> m Pandoc
    handleImages WriterOptions
    opts String
    tmpdir Pandoc
    doc
            source <- writer opts{ writerExtensions = -- disable use of quote
                                      -- ligatures to avoid bad ligatures like ?`
                                      disableExtension Ext_smart
                                       (writerExtensions opts) } doc'
            case baseProg of
              String
    "context" -> String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    context2pdf String
    program [String]
    pdfargs String
    tmpdir Text
    source
              String
    "tectonic" -> String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    tectonic2pdf String
    program [String]
    pdfargs String
    tmpdir Text
    source
              String
    prog | String
    prog String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [String
    "pdflatex", String
    "lualatex", String
    "xelatex", String
    "latexmk"]
                  -> String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    tex2pdf String
    program [String]
    pdfargs String
    tmpdir Text
    source
              String
    _ -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left (ByteString -> Either ByteString ByteString)
    -> ByteString -> Either ByteString ByteString
    forall a b. (a -> b) -> a -> b
    $ String -> ByteString
    UTF8.fromStringLazy
                                 (String -> ByteString) -> String -> ByteString
    forall a b. (a -> b) -> a -> b
    $ String
    "Unknown program " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    program

    -- latex has trouble with tildes in paths, which
    -- you find in Windows temp dir paths with longer
    -- user names (see #777)
    withTempDir :: (PandocMonad m, MonadMask m, MonadIO m)
                => FilePath -> (FilePath -> m a) -> m a
    withTempDir :: forall (m :: * -> *) a.
    (PandocMonad m, MonadMask m, MonadIO m) =>
    String -> (String -> m a) -> m a
    withTempDir String
    templ String -> m a
    action = do
      tmp <- IO String -> m String
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO String
    getTemporaryDirectory
      uname <- liftIO $ E.catch
        (do (ec, sout, _) <- readProcessWithExitCode "uname" ["-o"] ""
            if ec == ExitSuccess
               then return $ Just $ filter (not . isSpace) sout
               else return Nothing)
        (\(SomeException
    _  :: E.SomeException) -> Maybe String -> IO (Maybe String)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe String
    forall a. Maybe a
    Nothing)
      if '~' `elem` tmp || uname == Just "Cygwin" -- see #5451
             then withTempDirectory "." templ action
             else withSystemTempDirectory templ action

    makeWithWkhtmltopdf :: (PandocMonad m, MonadIO m)
                        => String              -- ^ wkhtmltopdf or path
                        -> [String]            -- ^ arguments
                        -> (WriterOptions -> Pandoc -> m Text)  -- ^ writer
                        -> WriterOptions       -- ^ options
                        -> Pandoc              -- ^ document
                        -> m (Either ByteString ByteString)
    makeWithWkhtmltopdf :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> (WriterOptions -> Pandoc -> m Text)
    -> WriterOptions
    -> Pandoc
    -> m (Either ByteString ByteString)
    makeWithWkhtmltopdf String
    program [String]
    pdfargs WriterOptions -> Pandoc -> m Text
    writer WriterOptions
    opts doc :: Pandoc
    doc@(Pandoc Meta
    meta [Block]
    _) = do
      let mathArgs :: [String]
    mathArgs = case WriterOptions -> HTMLMathMethod
    writerHTMLMathMethod WriterOptions
    opts of
                     -- with MathJax, wait til all math is rendered:
                          MathJax Text
    _ -> [String
    "--run-script", String
    "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });",
                                        String
    "--window-status", String
    "mathjax_loaded"]
                          HTMLMathMethod
    _ -> []
      meta' <- WriterOptions
    -> ([Block] -> m (Doc Text))
    -> ([Inline] -> m (Doc Text))
    -> Meta
    -> m (Context Text)
    forall (m :: * -> *) a.
    (Monad m, TemplateTarget a) =>
    WriterOptions
    -> ([Block] -> m (Doc a))
    -> ([Inline] -> m (Doc a))
    -> Meta
    -> m (Context a)
    metaToContext WriterOptions
    opts
                 (Doc Text -> m (Doc Text)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Doc Text -> m (Doc Text))
    -> ([Block] -> Doc Text) -> [Block] -> m (Doc Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Doc Text
    forall a. HasChars a => a -> Doc a
    literal (Text -> Doc Text) -> ([Block] -> Text) -> [Block] -> Doc Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Block] -> Text
    forall a. Walkable Inline a => a -> Text
    stringify)
                 (Doc Text -> m (Doc Text)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Doc Text -> m (Doc Text))
    -> ([Inline] -> Doc Text) -> [Inline] -> m (Doc Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Doc Text
    forall a. HasChars a => a -> Doc a
    literal (Text -> Doc Text) -> ([Inline] -> Text) -> [Inline] -> Doc Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Inline] -> Text
    forall a. Walkable Inline a => a -> Text
    stringify)
                 Meta
    meta
      let toArgs (String
    f, Maybe Text
    mbd) = [String] -> (Text -> [String]) -> Maybe Text -> [String]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [] (\Text
    d -> [String
    "--" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    f, Text -> String
    T.unpack Text
    d]) Maybe Text
    mbd
      let args   = [String]
    mathArgs [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ ((String, Maybe Text) -> [String])
    -> [(String, Maybe Text)] -> [String]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (String, Maybe Text) -> [String]
    toArgs
                     [(String
    "page-size", Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "papersize" Context Text
    meta')
                     ,(String
    "title", Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "title" Context Text
    meta')
                     ,(String
    "margin-bottom", Text -> Maybe Text
    forall a. a -> Maybe a
    Just (Text -> Maybe Text) -> Text -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "1.2in"
                                (Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "margin-bottom" Context Text
    meta'))
                     ,(String
    "margin-top", Text -> Maybe Text
    forall a. a -> Maybe a
    Just (Text -> Maybe Text) -> Text -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "1.25in"
                                (Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "margin-top" Context Text
    meta'))
                     ,(String
    "margin-right", Text -> Maybe Text
    forall a. a -> Maybe a
    Just (Text -> Maybe Text) -> Text -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "1.25in"
                                (Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "margin-right" Context Text
    meta'))
                     ,(String
    "margin-left", Text -> Maybe Text
    forall a. a -> Maybe a
    Just (Text -> Maybe Text) -> Text -> Maybe Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    "1.25in"
                                (Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "margin-left" Context Text
    meta'))
                     ,(String
    "footer-html", Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "footer-html" Context Text
    meta')
                     ,(String
    "header-html", Text -> Context Text -> Maybe Text
    forall a b. FromContext a b => Text -> Context a -> Maybe b
    getField Text
    "header-html" Context Text
    meta')
                     ] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ (String
    "--enable-local-file-access" String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : [String]
    pdfargs)
                     -- see #6474
      source <- writer opts doc
      verbosity <- getVerbosity
      liftIO $ toPdfViaTempFile verbosity program args (:[]) ".html" source

    handleImages :: (PandocMonad m, MonadIO m)
                 => WriterOptions
                 -> FilePath      -- ^ temp dir to store images
                 -> Pandoc        -- ^ document
                 -> m Pandoc
    handleImages :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    WriterOptions -> String -> Pandoc -> m Pandoc
    handleImages WriterOptions
    opts String
    tmpdir Pandoc
    doc =
      Pandoc -> m Pandoc
    forall (m :: * -> *). PandocMonad m => Pandoc -> m Pandoc
    fillMediaBag Pandoc
    doc m Pandoc -> (Pandoc -> m Pandoc) -> m Pandoc
    forall a b. m a -> (a -> m b) -> m b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>=
        String -> Pandoc -> m Pandoc
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String -> Pandoc -> m Pandoc
    extractMedia String
    tmpdir m Pandoc -> (Pandoc -> m Pandoc) -> m Pandoc
    forall a b. m a -> (a -> m b) -> m b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>=
        (Inline -> m Inline) -> Pandoc -> m Pandoc
    forall a b (m :: * -> *).
    (Walkable a b, Monad m, Applicative m, Functor m) =>
    (a -> m a) -> b -> m b
    forall (m :: * -> *).
    (Monad m, Applicative m, Functor m) =>
    (Inline -> m Inline) -> Pandoc -> m Pandoc
    walkM (WriterOptions -> String -> Inline -> m Inline
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    WriterOptions -> String -> Inline -> m Inline
    convertImages WriterOptions
    opts String
    tmpdir)

    convertImages :: (PandocMonad m, MonadIO m)
                  => WriterOptions -> FilePath -> Inline -> m Inline
    convertImages :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    WriterOptions -> String -> Inline -> m Inline
    convertImages WriterOptions
    opts String
    tmpdir (Image Attr
    attr [Inline]
    ils (Text
    src, Text
    tit)) = do
      img <- IO (Either Text String) -> m (Either Text String)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Either Text String) -> m (Either Text String))
    -> IO (Either Text String) -> m (Either Text String)
    forall a b. (a -> b) -> a -> b
    $ WriterOptions -> String -> String -> IO (Either Text String)
    convertImage WriterOptions
    opts String
    tmpdir (String -> IO (Either Text String))
    -> String -> IO (Either Text String)
    forall a b. (a -> b) -> a -> b
    $ Text -> String
    T.unpack Text
    src
      newPath <-
        case img of
          Left Text
    e -> do
            LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    CouldNotConvertImage Text
    src Text
    e
            Text -> m Text
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Text
    src
          Right String
    fp -> Text -> m Text
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> m Text) -> Text -> m Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack String
    fp
      return (Image attr ils (newPath, tit))
    convertImages WriterOptions
    _ String
    _ Inline
    x = Inline -> m Inline
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Inline
    x

    -- Convert formats which do not work well in pdf to png
    convertImage :: WriterOptions -> FilePath -> FilePath
                 -> IO (Either Text FilePath)
    convertImage :: WriterOptions -> String -> String -> IO (Either Text String)
    convertImage WriterOptions
    opts String
    tmpdir String
    fname = do
      let dpi :: String
    dpi = Int -> String
    forall a. Show a => a -> String
    show (Int -> String) -> Int -> String
    forall a b. (a -> b) -> a -> b
    $ WriterOptions -> Int
    writerDpi WriterOptions
    opts
      case Maybe Text
    mime of
        Just Text
    "image/png" -> IO (Either Text String)
    forall {a}. IO (Either a String)
    doNothing
        Just Text
    "image/jpeg" -> IO (Either Text String)
    forall {a}. IO (Either a String)
    doNothing
        Just Text
    "application/pdf" -> IO (Either Text String)
    forall {a}. IO (Either a String)
    doNothing
        -- Note: eps is converted by pdflatex using epstopdf.pl
        Just Text
    "application/eps" -> IO (Either Text String)
    forall {a}. IO (Either a String)
    doNothing
        Just Text
    "image/svg+xml" -> IO (Either Text String)
    -> (SomeException -> IO (Either Text String))
    -> IO (Either Text String)
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    E.catch (do
          (exit, _) <- Maybe [(String, String)]
    -> String -> [String] -> ByteString -> IO (ExitCode, ByteString)
    pipeProcess Maybe [(String, String)]
    forall a. Maybe a
    Nothing String
    "rsvg-convert"
                         [String
    "-f",String
    "pdf",String
    "-a",String
    "--dpi-x",String
    dpi,String
    "--dpi-y",String
    dpi,
                          String
    "-o",String
    pdfOut,String
    svgIn] ByteString
    BL.empty
          if exit == ExitSuccess
             then return $ Right pdfOut
             else return $ Left "conversion from SVG failed")
          (\(SomeException
    e :: E.SomeException) -> Either Text String -> IO (Either Text String)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either Text String -> IO (Either Text String))
    -> Either Text String -> IO (Either Text String)
    forall a b. (a -> b) -> a -> b
    $ Text -> Either Text String
    forall a b. a -> Either a b
    Left (Text -> Either Text String) -> Text -> Either Text String
    forall a b. (a -> b) -> a -> b
    $
              Text
    "check that rsvg-convert is in path.\n" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>
              SomeException -> Text
    forall a. Show a => a -> Text
    tshow SomeException
    e)
        Maybe Text
    _ -> String -> IO (Either String DynamicImage)
    JP.readImage String
    fname IO (Either String DynamicImage)
    -> (Either String DynamicImage -> IO (Either Text String))
    -> IO (Either Text String)
    forall a b. IO a -> (a -> IO b) -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
                   Left String
    e    -> Either Text String -> IO (Either Text String)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either Text String -> IO (Either Text String))
    -> Either Text String -> IO (Either Text String)
    forall a b. (a -> b) -> a -> b
    $ Text -> Either Text String
    forall a b. a -> Either a b
    Left (Text -> Either Text String) -> Text -> Either Text String
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack String
    e
                   Right DynamicImage
    img ->
                     IO (Either Text String)
    -> (SomeException -> IO (Either Text String))
    -> IO (Either Text String)
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    E.catch (String -> Either Text String
    forall a b. b -> Either a b
    Right String
    pngOut Either Text String -> IO () -> IO (Either Text String)
    forall a b. a -> IO b -> IO a
    forall (f :: * -> *) a b. Functor f => a -> f b -> f a
    <$ String -> DynamicImage -> IO ()
    JP.savePngImage String
    pngOut DynamicImage
    img) ((SomeException -> IO (Either Text String))
     -> IO (Either Text String))
    -> (SomeException -> IO (Either Text String))
    -> IO (Either Text String)
    forall a b. (a -> b) -> a -> b
    $
                         \(SomeException
    e :: E.SomeException) -> Either Text String -> IO (Either Text String)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> Either Text String
    forall a b. a -> Either a b
    Left (SomeException -> Text
    forall a. Show a => a -> Text
    tshow SomeException
    e))
      where
        sha :: String
    sha = Digest SHA1 -> String
    forall a. Show a => a -> String
    show (SHA1 -> ByteString -> Digest SHA1
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith SHA1
    SHA1 (String -> ByteString
    UTF8.fromString String
    fname))
        pngOut :: String
    pngOut = String -> String
    normalise (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String
    tmpdir String -> String -> String
    </> String
    sha String -> String -> String
    <.> String
    "png"
        pdfOut :: String
    pdfOut = String -> String
    normalise (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String
    tmpdir String -> String -> String
    </> String
    sha String -> String -> String
    <.> String
    "pdf"
        svgIn :: String
    svgIn = String -> String
    normalise String
    fname
        mime :: Maybe Text
    mime = String -> Maybe Text
    getMimeType String
    fname
        doNothing :: IO (Either a String)
    doNothing = Either a String -> IO (Either a String)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (String -> Either a String
    forall a b. b -> Either a b
    Right String
    fname)

    tectonic2pdf :: (PandocMonad m, MonadIO m)
                 => String                          -- ^ tex program
                 -> [String]                        -- ^ Arguments to the latex-engine
                 -> FilePath                        -- ^ temp directory for output
                 -> Text                            -- ^ tex source
                 -> m (Either ByteString ByteString)
    tectonic2pdf :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    tectonic2pdf String
    program [String]
    args String
    tmpDir Text
    source = do
      (exit, log', mbPdf) <- String
    -> [String]
    -> String
    -> Text
    -> m (ExitCode, ByteString, Maybe ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> String
    -> Text
    -> m (ExitCode, ByteString, Maybe ByteString)
    runTectonic String
    program [String]
    args String
    tmpDir Text
    source
      case (exit, mbPdf) of
           (ExitFailure Int
    _, Maybe ByteString
    _)      -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left (ByteString -> Either ByteString ByteString)
    -> ByteString -> Either ByteString ByteString
    forall a b. (a -> b) -> a -> b
    $ ByteString -> ByteString
    extractMsg ByteString
    log'
           (ExitCode
    ExitSuccess, Maybe ByteString
    Nothing)  -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    ""
           (ExitCode
    ExitSuccess, Just ByteString
    pdf) -> do
              ByteString -> m ()
    forall (m :: * -> *). PandocMonad m => ByteString -> m ()
    missingCharacterWarnings ByteString
    log'
              Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. b -> Either a b
    Right ByteString
    pdf

    tex2pdf :: (PandocMonad m, MonadIO m)
            => String                          -- ^ tex program
            -> [String]                        -- ^ Arguments to the latex-engine
            -> FilePath                        -- ^ temp directory for output
            -> Text                            -- ^ tex source
            -> m (Either ByteString ByteString)
    tex2pdf :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    tex2pdf String
    program [String]
    args String
    tmpDir Text
    source = do
      let isOutdirArg :: String -> Bool
    isOutdirArg String
    x = String
    "-outdir=" String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` String
    x Bool -> Bool -> Bool
    ||
                          String
    "-output-directory=" String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` String
    x
      let outDir :: String
    outDir =
            case (String -> Bool) -> [String] -> Maybe String
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    find String -> Bool
    isOutdirArg [String]
    args of
              Just String
    x  -> Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /=Char
    '=') String
    x
              Maybe String
    Nothing -> String
    tmpDir
      let file :: String
    file = String
    tmpDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "/input.tex"  -- note: tmpDir has / path separators
      IO () -> m ()
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ String -> ByteString -> IO ()
    BS.writeFile String
    file (ByteString -> IO ()) -> ByteString -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text -> ByteString
    UTF8.fromText Text
    source
      (exit, log', mbPdf) <- String
    -> [String]
    -> String
    -> String
    -> m (ExitCode, ByteString, Maybe ByteString)
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> String
    -> String
    -> m (ExitCode, ByteString, Maybe ByteString)
    runTeXProgram String
    program [String]
    args String
    tmpDir String
    outDir
      case (exit, mbPdf) of
           (ExitFailure Int
    _, Maybe ByteString
    _)      -> do
              let logmsg :: ByteString
    logmsg = ByteString -> ByteString
    extractMsg ByteString
    log'
              let extramsg :: ByteString
    extramsg =
                    case ByteString
    logmsg of
                         ByteString
    x | ByteString
    "! Package inputenc Error" ByteString -> ByteString -> Bool
    `BC.isPrefixOf` ByteString
    x
                               Bool -> Bool -> Bool
    && String
    program String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    /= String
    "xelatex"
                           -> ByteString
    "\nTry running pandoc with --pdf-engine=xelatex."
                         ByteString
    _ -> ByteString
    ""
              Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left (ByteString -> Either ByteString ByteString)
    -> ByteString -> Either ByteString ByteString
    forall a b. (a -> b) -> a -> b
    $ ByteString
    logmsg ByteString -> ByteString -> ByteString
    forall a. Semigroup a => a -> a -> a
    <> ByteString
    extramsg
           (ExitCode
    ExitSuccess, Maybe ByteString
    Nothing)  -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    ""
           (ExitCode
    ExitSuccess, Just ByteString
    pdf) -> do
              ByteString -> m ()
    forall (m :: * -> *). PandocMonad m => ByteString -> m ()
    latexWarnings ByteString
    log'
              ByteString -> m ()
    forall (m :: * -> *). PandocMonad m => ByteString -> m ()
    missingCharacterWarnings ByteString
    log'
              Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> m (Either ByteString ByteString))
    -> Either ByteString ByteString -> m (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. b -> Either a b
    Right ByteString
    pdf

    missingCharacterWarnings :: PandocMonad m => ByteString -> m ()
    missingCharacterWarnings :: forall (m :: * -> *). PandocMonad m => ByteString -> m ()
    missingCharacterWarnings ByteString
    log' = do
      let ls :: [ByteString]
    ls = ByteString -> [ByteString]
    BC.lines ByteString
    log'
      let isMissingCharacterWarning :: ByteString -> Bool
    isMissingCharacterWarning = ByteString -> ByteString -> Bool
    BC.isPrefixOf ByteString
    "Missing character:"
      let toCodePoint :: Char -> Text
    toCodePoint Char
    c
            | Char -> Bool
    isAscii Char
    c   = Char -> Text
    T.singleton Char
    c
            | Bool
    otherwise   = String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ Char
    c Char -> String -> String
    forall a. a -> [a] -> [a]
    : String
    " (U+" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String -> Int -> String
    forall r. PrintfType r => String -> r
    printf String
    "%04X" (Char -> Int
    ord Char
    c) String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    ")"
      let addCodePoint :: Text -> Text
    addCodePoint = (Char -> Text) -> Text -> Text
    T.concatMap Char -> Text
    toCodePoint
      let warnings :: [Text]
    warnings = [ Text -> Text
    addCodePoint (ByteString -> Text
    utf8ToText (Int64 -> ByteString -> ByteString
    BC.drop Int64
    19 ByteString
    l))
                     | ByteString
    l <- [ByteString]
    ls
                     , ByteString -> Bool
    isMissingCharacterWarning ByteString
    l
                     ]
      (Text -> m ()) -> [Text] -> m ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ (LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> (Text -> LogMessage) -> Text -> m ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> LogMessage
    MissingCharacter) [Text]
    warnings

    latexWarnings :: PandocMonad m => ByteString -> m ()
    latexWarnings :: forall (m :: * -> *). PandocMonad m => ByteString -> m ()
    latexWarnings ByteString
    log' = (Maybe ByteString -> ByteString -> m (Maybe ByteString))
    -> Maybe ByteString -> [ByteString] -> m ()
    forall (t :: * -> *) (m :: * -> *) b a.
    (Foldable t, Monad m) =>
    (b -> a -> m b) -> b -> t a -> m ()
    foldM_ Maybe ByteString -> ByteString -> m (Maybe ByteString)
    forall {f :: * -> *}.
    PandocMonad f =>
    Maybe ByteString -> ByteString -> f (Maybe ByteString)
    go Maybe ByteString
    forall a. Maybe a
    Nothing (ByteString -> [ByteString]
    BC.lines ByteString
    log')
     where
       go :: Maybe ByteString -> ByteString -> f (Maybe ByteString)
    go Maybe ByteString
    Nothing ByteString
    ln
         | ByteString -> ByteString -> Bool
    BC.isPrefixOf ByteString
    "LaTeX Warning:" ByteString
    ln =
           Maybe ByteString -> f (Maybe ByteString)
    forall a. a -> f a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (Maybe ByteString -> f (Maybe ByteString))
    -> Maybe ByteString -> f (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Maybe ByteString
    forall a. a -> Maybe a
    Just ByteString
    ln
         | Bool
    otherwise = Maybe ByteString -> f (Maybe ByteString)
    forall a. a -> f a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Maybe ByteString
    forall a. Maybe a
    Nothing
       go (Just ByteString
    msg) ByteString
    ln
         | ByteString
    ln ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    "" = do -- emit report and reset accumulator
             LogMessage -> f ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> f ()) -> LogMessage -> f ()
    forall a b. (a -> b) -> a -> b
    $ Text -> LogMessage
    MakePDFWarning (Text -> LogMessage) -> Text -> LogMessage
    forall a b. (a -> b) -> a -> b
    $ Maybe Int -> Doc Text -> Text
    forall a. HasChars a => Maybe Int -> Doc a -> a
    render (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    60) (Doc Text -> Text) -> Doc Text -> Text
    forall a b. (a -> b) -> a -> b
    $
                [Doc Text] -> Doc Text
    forall a. [Doc a] -> Doc a
    hsep ([Doc Text] -> Doc Text) -> [Doc Text] -> Doc Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Doc Text) -> [Text] -> [Doc Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Doc Text
    forall a. HasChars a => a -> Doc a
    literal ([Text] -> [Doc Text]) -> [Text] -> [Doc Text]
    forall a b. (a -> b) -> a -> b
    $ Text -> [Text]
    T.words (Text -> [Text]) -> Text -> [Text]
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Text
    UTF8.toText (ByteString -> Text) -> ByteString -> Text
    forall a b. (a -> b) -> a -> b
    $ ByteString -> ByteString
    BC.toStrict ByteString
    msg
             Maybe ByteString -> f (Maybe ByteString)
    forall a. a -> f a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Maybe ByteString
    forall a. Maybe a
    Nothing
         | Bool
    otherwise = Maybe ByteString -> f (Maybe ByteString)
    forall a. a -> f a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (Maybe ByteString -> f (Maybe ByteString))
    -> Maybe ByteString -> f (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Maybe ByteString
    forall a. a -> Maybe a
    Just (ByteString
    msg ByteString -> ByteString -> ByteString
    forall a. Semigroup a => a -> a -> a
    <> ByteString
    ln)

    -- parsing output

    extractMsg :: ByteString -> ByteString
    extractMsg :: ByteString -> ByteString
    extractMsg ByteString
    log' = do
      let msg' :: [ByteString]
    msg'  = (ByteString -> Bool) -> [ByteString] -> [ByteString]
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Bool -> Bool
    not (Bool -> Bool) -> (ByteString -> Bool) -> ByteString -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ByteString
    "!" ByteString -> ByteString -> Bool
    `BC.isPrefixOf`)) ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
    forall a b. (a -> b) -> a -> b
    $ ByteString -> [ByteString]
    BC.lines ByteString
    log'
      let ([ByteString]
    msg'',[ByteString]
    rest) = (ByteString -> Bool)
    -> [ByteString] -> ([ByteString], [ByteString])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    break (ByteString
    "l." ByteString -> ByteString -> Bool
    `BC.isPrefixOf`) [ByteString]
    msg'
      let lineno :: [ByteString]
    lineno = Int -> [ByteString] -> [ByteString]
    forall a. Int -> [a] -> [a]
    take Int
    1 [ByteString]
    rest
      if [ByteString] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [ByteString]
    msg'
         then ByteString
    log'
         else [ByteString] -> ByteString
    BC.unlines ([ByteString]
    msg'' [ByteString] -> [ByteString] -> [ByteString]
    forall a. [a] -> [a] -> [a]
    ++ [ByteString]
    lineno)

    extractConTeXtMsg :: ByteString -> ByteString
    extractConTeXtMsg :: ByteString -> ByteString
    extractConTeXtMsg ByteString
    log' = do
      let msg' :: [ByteString]
    msg'  = Int -> [ByteString] -> [ByteString]
    forall a. Int -> [a] -> [a]
    take Int
    1 ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
    forall a b. (a -> b) -> a -> b
    $
                  (ByteString -> Bool) -> [ByteString] -> [ByteString]
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Bool -> Bool
    not (Bool -> Bool) -> (ByteString -> Bool) -> ByteString -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ByteString
    "tex error" ByteString -> ByteString -> Bool
    `BC.isPrefixOf`)) ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
    forall a b. (a -> b) -> a -> b
    $ ByteString -> [ByteString]
    BC.lines ByteString
    log'
      if [ByteString] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [ByteString]
    msg'
         then ByteString
    log'
         else [ByteString] -> ByteString
    BC.unlines [ByteString]
    msg'

    -- running tex programs

    runTectonic :: (PandocMonad m, MonadIO m)
                => String -> [String] -> FilePath
                  -> Text -> m (ExitCode, ByteString, Maybe ByteString)
    runTectonic :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> String
    -> Text
    -> m (ExitCode, ByteString, Maybe ByteString)
    runTectonic String
    program [String]
    args' String
    tmpDir' Text
    source = do
        let getOutDir :: [a] -> [a] -> ([a], Maybe a)
    getOutDir [a]
    acc (a
    a:a
    b:[a]
    xs) = if a
    a a -> [a] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [a
    "-o", a
    "--outdir"]
                                        then ([a] -> [a]
    forall a. [a] -> [a]
    reverse [a]
    acc [a] -> [a] -> [a]
    forall a. [a] -> [a] -> [a]
    ++ [a]
    xs, a -> Maybe a
    forall a. a -> Maybe a
    Just a
    b)
                                        else [a] -> [a] -> ([a], Maybe a)
    getOutDir (a
    ba -> [a] -> [a]
    forall a. a -> [a] -> [a]
    :a
    aa -> [a] -> [a]
    forall a. a -> [a] -> [a]
    :[a]
    acc) [a]
    xs
            getOutDir [a]
    acc [a]
    xs = ([a] -> [a]
    forall a. [a] -> [a]
    reverse [a]
    acc [a] -> [a] -> [a]
    forall a. [a] -> [a] -> [a]
    ++ [a]
    xs, Maybe a
    forall a. Maybe a
    Nothing)
            ([String]
    args, Maybe String
    outDir) = [String] -> [String] -> ([String], Maybe String)
    forall {a}. (Eq a, IsString a) => [a] -> [a] -> ([a], Maybe a)
    getOutDir [] [String]
    args'
            tmpDir :: String
    tmpDir = String -> Maybe String -> String
    forall a. a -> Maybe a -> a
    fromMaybe String
    tmpDir' Maybe String
    outDir
        IO () -> m ()
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> m ()) -> IO () -> m ()
    forall a b. (a -> b) -> a -> b
    $ Bool -> String -> IO ()
    createDirectoryIfMissing Bool
    True String
    tmpDir
        -- run tectonic on stdin so it reads \include commands from $PWD instead of a temp directory
        let sourceBL :: ByteString
    sourceBL = ByteString -> ByteString
    BL.fromStrict (ByteString -> ByteString) -> ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Text -> ByteString
    UTF8.fromText Text
    source
        let programArgs :: [String]
    programArgs = [String
    "--outdir", String
    tmpDir] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    args [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    "-"]
        env <- IO [(String, String)] -> m [(String, String)]
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO [(String, String)]
    getEnvironment
        showVerboseInfo (Just tmpDir) program programArgs env (utf8ToText sourceBL)
        (exit, out) <- liftIO $ E.catch
          (pipeProcess (Just env) program programArgs sourceBL)
          (handlePDFProgramNotFound program)
        report $ MakePDFInfo "tectonic output" (UTF8.toText $ BL.toStrict out)
        let pdfFile = String
    tmpDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "/texput.pdf"
        (_, pdf) <- getResultingPDF Nothing pdfFile
        return (exit, out, pdf)

    -- read a pdf that has been written to a temporary directory, and optionally read
    -- logs
    getResultingPDF :: (PandocMonad m, MonadIO m)
                    => Maybe String -> String
                    -> m (Maybe ByteString, Maybe ByteString)
    getResultingPDF :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    Maybe String -> String -> m (Maybe ByteString, Maybe ByteString)
    getResultingPDF Maybe String
    logFile String
    pdfFile = do
        pdfExists <- String -> m Bool
    forall (m :: * -> *). PandocMonad m => String -> m Bool
    fileExists String
    pdfFile
        pdf <- if pdfExists
                  -- We read PDF as a strict bytestring to make sure that the
                  -- temp directory is removed on Windows.
                  -- See https://github.com/jgm/pandoc/issues/1192.
                  then (Just . BL.fromChunks . (:[])) `fmap`
                       (readFileStrict pdfFile)
                  else return Nothing
        -- Note that some things like Missing character warnings
        -- appear in the log but not on stderr, so we prefer the log:
        log' <- case logFile of
                  Just String
    logFile' -> do
                    logExists <- String -> m Bool
    forall (m :: * -> *). PandocMonad m => String -> m Bool
    fileExists String
    logFile'
                    if logExists
                      then Just . BL.fromStrict <$> readFileStrict logFile'
                      else return Nothing
                  Maybe String
    Nothing -> Maybe ByteString -> m (Maybe ByteString)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe ByteString
    forall a. Maybe a
    Nothing
        return (log', pdf)

    -- Run a TeX program once in a temp directory (on input.tex) and return (exit code,
    -- contents of stdout, contents of produced PDF if any).
    runTeXProgram :: (PandocMonad m, MonadIO m)
                  => String -> [String] -> FilePath -> FilePath
                  -> m (ExitCode, ByteString, Maybe ByteString)
    runTeXProgram :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String]
    -> String
    -> String
    -> m (ExitCode, ByteString, Maybe ByteString)
    runTeXProgram String
    program [String]
    args String
    tmpDir String
    outDir = do
        let isLatexMk :: Bool
    isLatexMk = String -> String
    takeBaseName String
    program String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    == String
    "latexmk"
            programArgs :: [String]
    programArgs | Bool
    isLatexMk =
                          [String
    "-interaction=batchmode", String
    "-halt-on-error", String
    "-pdf",
                           String
    "-quiet", String
    "-outdir=" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    outDir] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    args [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    file]
                        | Bool
    otherwise =
                          [String
    "-halt-on-error", String
    "-interaction", String
    "nonstopmode",
                           String
    "-output-directory", String
    outDir] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    args [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    file]
        env' <- IO [(String, String)] -> m [(String, String)]
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO [(String, String)]
    getEnvironment
        let sep = [Char
    searchPathSeparator]
        let texinputs = String -> (String -> String) -> Maybe String -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe (String
    tmpDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    sep) ((String
    tmpDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    sep) String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++)
              (Maybe String -> String) -> Maybe String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> [(String, String)] -> Maybe String
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup String
    "TEXINPUTS" [(String, String)]
    env'
        let env'' = (String
    "TEXINPUTS", String
    texinputs) (String, String) -> [(String, String)] -> [(String, String)]
    forall a. a -> [a] -> [a]
    :
                    (String
    "TEXMFOUTPUT", String
    outDir) (String, String) -> [(String, String)] -> [(String, String)]
    forall a. a -> [a] -> [a]
    :
                      [(String
    k,String
    v) | (String
    k,String
    v) <- [(String, String)]
    env'
                             , String
    k String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    /= String
    "TEXINPUTS" Bool -> Bool -> Bool
    && String
    k String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    /= String
    "TEXMFOUTPUT"]
        liftIO (UTF8.readFile file) >>=
          showVerboseInfo (Just tmpDir) program programArgs env''
        go env'' programArgs (1 :: Int)
     where
       file :: String
    file = String
    tmpDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "/input.tex"
       outfile :: String
    outfile = String
    outDir String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "/input.pdf"
       go :: [(String, String)]
    -> [String] -> t -> m (ExitCode, ByteString, Maybe ByteString)
    go [(String, String)]
    env'' [String]
    programArgs t
    runNumber = do
         let maxruns :: t
    maxruns = t
    4 -- stop if warnings present after 4 runs
         LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    MakePDFInfo (Text
    "LaTeX run number " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> t -> Text
    forall a. Show a => a -> Text
    tshow t
    runNumber) Text
    forall a. Monoid a => a
    mempty
         (exit, out) <- IO (ExitCode, ByteString) -> m (ExitCode, ByteString)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (ExitCode, ByteString) -> m (ExitCode, ByteString))
    -> IO (ExitCode, ByteString) -> m (ExitCode, ByteString)
    forall a b. (a -> b) -> a -> b
    $ IO (ExitCode, ByteString)
    -> (IOError -> IO (ExitCode, ByteString))
    -> IO (ExitCode, ByteString)
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    E.catch
           (Maybe [(String, String)]
    -> String -> [String] -> ByteString -> IO (ExitCode, ByteString)
    pipeProcess ([(String, String)] -> Maybe [(String, String)]
    forall a. a -> Maybe a
    Just [(String, String)]
    env'') String
    program [String]
    programArgs ByteString
    BL.empty)
           (String -> IOError -> IO (ExitCode, ByteString)
    forall a. String -> IOError -> IO a
    handlePDFProgramNotFound String
    program)
         report $ MakePDFInfo "LaTeX output" (UTF8.toText $ BL.toStrict out)
         -- parse log to see if we need to rerun LaTeX
         let logFile = String -> String -> String
    replaceExtension String
    outfile String
    ".log"
         logExists <- fileExists logFile
         logContents <- if logExists
                           then BL.fromStrict <$> readFileStrict logFile
                           else return mempty
         let rerunWarnings = ByteString -> [ByteString]
    checkForRerun ByteString
    logContents
         tocFileExists <- fileExists (replaceExtension outfile ".toc")
           -- if we have a TOC we always need 3 runs, see #10308
         let rerunWarnings' = [ByteString]
    rerunWarnings [ByteString] -> [ByteString] -> [ByteString]
    forall a. [a] -> [a] -> [a]
    ++ [ByteString
    "TOC is present" | Bool
    tocFileExists]
         if not (null rerunWarnings') && runNumber < maxruns
            then do
              report $ MakePDFInfo "Rerun needed"
                        (T.intercalate "\n"
                          (map (UTF8.toText . BC.toStrict) rerunWarnings'))
              go env'' programArgs (runNumber + 1)
           else do
              (log', pdf) <- getResultingPDF (Just logFile) outfile
              return (exit, fromMaybe out log', pdf)

       checkForRerun :: ByteString -> [ByteString]
    checkForRerun ByteString
    log' = (ByteString -> Bool) -> [ByteString] -> [ByteString]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ByteString -> Bool
    isRerunWarning ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
    forall a b. (a -> b) -> a -> b
    $ ByteString -> [ByteString]
    BC.lines ByteString
    log'

       isRerunWarning :: ByteString -> Bool
    isRerunWarning ByteString
    ln =
         let ln' :: ByteString
    ln' = ByteString -> ByteString
    BL.toStrict ByteString
    ln
           in ByteString -> ByteString -> Bool
    BS.isInfixOf ByteString
    "Warning:" ByteString
    ln' Bool -> Bool -> Bool
    && ByteString -> ByteString -> Bool
    BS.isInfixOf ByteString
    "Rerun" ByteString
    ln'

    generic2pdf :: (PandocMonad m, MonadIO m)
                => String
                -> [String]
                -> Text
                -> m (Either ByteString ByteString)
    generic2pdf :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String -> [String] -> Text -> m (Either ByteString ByteString)
    generic2pdf String
    program [String]
    args Text
    source = do
      env' <- IO [(String, String)] -> m [(String, String)]
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO [(String, String)]
    getEnvironment
      showVerboseInfo Nothing program args env' source
      (exit, out) <- liftIO $ E.catch
        (pipeProcess (Just env') program args
                         (BL.fromStrict $ UTF8.fromText source))
        (handlePDFProgramNotFound program)
      return $ case exit of
                 ExitFailure Int
    _ -> ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    out
                 ExitCode
    ExitSuccess   -> ByteString -> Either ByteString ByteString
    forall a b. b -> Either a b
    Right ByteString
    out

    toPdfViaTempFile  ::
                 Verbosity    -- ^ Verbosity level
              -> String       -- ^ Program (program name or path)
              -> [String]     -- ^ Args to program
              -> (String -> [String]) -- ^ Construct args for output file
              -> String       -- ^ extension to use for input file (e.g. '.html')
              -> Text         -- ^ Source
              -> IO (Either ByteString ByteString)
    toPdfViaTempFile :: Verbosity
    -> String
    -> [String]
    -> (String -> [String])
    -> String
    -> Text
    -> IO (Either ByteString ByteString)
    toPdfViaTempFile Verbosity
    verbosity String
    program [String]
    args String -> [String]
    mkOutArgs String
    extension Text
    source =
      String
    -> String
    -> (String -> Handle -> IO (Either ByteString ByteString))
    -> IO (Either ByteString ByteString)
    forall (m :: * -> *) a.
    (MonadIO m, MonadMask m) =>
    String -> String -> (String -> Handle -> m a) -> m a
    withTempFile String
    "." (String
    "toPdfViaTempFile" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    extension) ((String -> Handle -> IO (Either ByteString ByteString))
     -> IO (Either ByteString ByteString))
    -> (String -> Handle -> IO (Either ByteString ByteString))
    -> IO (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ \String
    file Handle
    h1 ->
        String
    -> String
    -> (String -> Handle -> IO (Either ByteString ByteString))
    -> IO (Either ByteString ByteString)
    forall (m :: * -> *) a.
    (MonadIO m, MonadMask m) =>
    String -> String -> (String -> Handle -> m a) -> m a
    withTempFile String
    "." String
    "toPdfViaTempFile.pdf" ((String -> Handle -> IO (Either ByteString ByteString))
     -> IO (Either ByteString ByteString))
    -> (String -> Handle -> IO (Either ByteString ByteString))
    -> IO (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ \String
    pdfFile Handle
    h2 -> do
          Handle -> IO ()
    hClose Handle
    h1
          Handle -> IO ()
    hClose Handle
    h2
          String -> ByteString -> IO ()
    BS.writeFile String
    file (ByteString -> IO ()) -> ByteString -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text -> ByteString
    UTF8.fromText Text
    source
          let programArgs :: [String]
    programArgs = [String]
    args [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    file] [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ String -> [String]
    mkOutArgs String
    pdfFile
          env' <- IO [(String, String)]
    getEnvironment
          fileContents <- UTF8.readFile file
          runIOorExplode $ do
            setVerbosity verbosity
            showVerboseInfo Nothing program programArgs env' fileContents
          (exit, out) <- E.catch
            (pipeProcess (Just env') program programArgs BL.empty)
            (handlePDFProgramNotFound program)
          runIOorExplode $ do
            setVerbosity verbosity
            report $ MakePDFInfo "pdf-engine output" (UTF8.toText $ BL.toStrict out)
          pdfExists <- doesFileExist pdfFile
          mbPdf <- if pdfExists
                    -- We read PDF as a strict bytestring to make sure that the
                    -- temp directory is removed on Windows.
                    -- See https://github.com/jgm/pandoc/issues/1192.
                    then Just . BL.fromChunks . (:[]) <$> BS.readFile pdfFile
                    else return Nothing
          return $ case (exit, mbPdf) of
                     (ExitFailure Int
    _, Maybe ByteString
    _)      -> ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    out
                     (ExitCode
    ExitSuccess, Maybe ByteString
    Nothing)  -> ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    ""
                     (ExitCode
    ExitSuccess, Just ByteString
    pdf) -> ByteString -> Either ByteString ByteString
    forall a b. b -> Either a b
    Right ByteString
    pdf

    context2pdf :: (PandocMonad m, MonadIO m)
                => String       -- ^ "context" or path to it
                -> [String]     -- ^ extra arguments
                -> FilePath     -- ^ temp directory for output
                -> Text         -- ^ ConTeXt source
                -> m (Either ByteString ByteString)
    context2pdf :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String
    -> [String] -> String -> Text -> m (Either ByteString ByteString)
    context2pdf String
    program [String]
    pdfargs String
    tmpDir Text
    source = do
      let file :: String
    file = String
    "input.tex"
      let programArgs :: [String]
    programArgs = String
    "--batchmode" String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    : [String]
    pdfargs [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    file]
      env' <- IO [(String, String)] -> m [(String, String)]
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO [(String, String)]
    getEnvironment
      verbosity <- getVerbosity
      liftIO $ BS.writeFile (tmpDir </> file) $ UTF8.fromText source
      liftIO (UTF8.readFile (tmpDir </> file)) >>=
        showVerboseInfo (Just tmpDir) program programArgs env'
      liftIO $ inDirectory tmpDir $ do
        (exit, out) <- E.catch
          (pipeProcess (Just env') program programArgs BL.empty)
          (handlePDFProgramNotFound program)
        runIOorExplode $ do
          setVerbosity verbosity
          report $ MakePDFInfo "ConTeXt run output" (UTF8.toText $ BL.toStrict out)
        let pdfFile = String -> String -> String
    replaceExtension String
    file String
    ".pdf"
        pdfExists <- doesFileExist pdfFile
        mbPdf <- if pdfExists
                  -- We read PDF as a strict bytestring to make sure that the
                  -- temp directory is removed on Windows.
                  -- See https://github.com/jgm/pandoc/issues/1192.
                  then (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile
                  else return Nothing
        case (exit, mbPdf) of
             (ExitFailure Int
    _, Maybe ByteString
    _)      -> do
                let logmsg :: ByteString
    logmsg = ByteString -> ByteString
    extractConTeXtMsg ByteString
    out
                Either ByteString ByteString -> IO (Either ByteString ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> IO (Either ByteString ByteString))
    -> Either ByteString ByteString
    -> IO (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    logmsg
             (ExitCode
    ExitSuccess, Maybe ByteString
    Nothing)  -> Either ByteString ByteString -> IO (Either ByteString ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> IO (Either ByteString ByteString))
    -> Either ByteString ByteString
    -> IO (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. a -> Either a b
    Left ByteString
    ""
             (ExitCode
    ExitSuccess, Just ByteString
    pdf) -> Either ByteString ByteString -> IO (Either ByteString ByteString)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either ByteString ByteString -> IO (Either ByteString ByteString))
    -> Either ByteString ByteString
    -> IO (Either ByteString ByteString)
    forall a b. (a -> b) -> a -> b
    $ ByteString -> Either ByteString ByteString
    forall a b. b -> Either a b
    Right ByteString
    pdf

    showVerboseInfo :: PandocMonad m
                    => Maybe FilePath
                    -> String
                    -> [String]
                    -> [(String, String)]
                    -> Text
                    -> m ()
    showVerboseInfo :: forall (m :: * -> *).
    PandocMonad m =>
    Maybe String
    -> String -> [String] -> [(String, String)] -> Text -> m ()
    showVerboseInfo Maybe String
    mbTmpDir String
    program [String]
    programArgs [(String, String)]
    env Text
    source = do
      case Maybe String
    mbTmpDir of
        Just String
    tmpDir -> LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    MakePDFInfo Text
    "Temp dir:" (String -> Text
    T.pack String
    tmpDir)
        Maybe String
    Nothing -> () -> m ()
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
      LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    MakePDFInfo Text
    "Command line:"
               (String -> Text
    T.pack String
    program Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> String -> Text
    T.pack ([String] -> String
    unwords ((String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    forall a. Show a => a -> String
    show [String]
    programArgs)))
      -- we filter out irrelevant stuff to avoid leaking passwords and keys!
      let isRelevant :: String -> Bool
    isRelevant String
    e = (String
    e String -> [String] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` [ String
    "PKFONTS"
                                   , String
    "AFMFONTS"
                                   , String
    "BIBINPUTS"
                                   , String
    "BLTXMLINPUTS"
                                   , String
    "BSTINPUTS"
                                   , String
    "CLUAINPUTS"
                                   , String
    "CMAPFONTS"
                                   , String
    "CWEBINPUTS"
                                   , String
    "DVIPSHEADERS"
                                   , String
    "ENCFONTS"
                                   , String
    "FONTCIDMAPS"
                                   , String
    "FONTFEATURES"
                                   , String
    "GFFONTS"
                                   , String
    "GLYPHFONTS"
                                   , String
    "HOME"
                                   , String
    "INDEXSTYLE"
                                   , String
    "KPATHSEA_DEBUG"
                                   , String
    "KPATHSEA_WARNING"
                                   , String
    "LANG"
                                   , String
    "LIGFONTS"
                                   , String
    "LUAINPUTS"
                                   , String
    "LUA_CPATH"
                                   , String
    "LUA_PATH"
                                   , String
    "MFBASES"
                                   , String
    "MFINPUTS"
                                   , String
    "MFPOOL"
                                   , String
    "MFTINPUTS"
                                   , String
    "MISCFONTS"
                                   , String
    "MISSFONT_LOG"
                                   , String
    "MLBIBINPUTS"
                                   , String
    "MLBSTINPUTS"
                                   , String
    "MPINPUTS"
                                   , String
    "MPMEMS"
                                   , String
    "MPPOOL"
                                   , String
    "MPSUPPORT"
                                   , String
    "OCPINPUTS"
                                   , String
    "OFMFONTS"
                                   , String
    "OPENTYPEFONTS"
                                   , String
    "OPLFONTS"
                                   , String
    "OTPINPUTS"
                                   , String
    "OVFFONTS"
                                   , String
    "OVPFONTS"
                                   , String
    "PATH"
                                   , String
    "PDFTEXCONFIG"
                                   , String
    "PROGRAMFONTS"
                                   , String
    "PSHEADERS"
                                   , String
    "PWD"
                                   , String
    "RISINPUTS"
                                   , String
    "SELFAUTODIR"
                                   , String
    "SELFAUTOLOC"
                                   , String
    "SELFAUTOPARENT"
                                   , String
    "SFDFONTS"
                                   , String
    "SHELL"
                                   , String
    "T1FONTS"
                                   , String
    "T1INPUTS"
                                   , String
    "T42FONTS"
                                   , String
    "TEXBIB"
                                   , String
    "TEXCONFIG"
                                   , String
    "TEXDOCS"
                                   , String
    "TEXFONTMAPS"
                                   , String
    "TEXFONTS"
                                   , String
    "TEXFORMATS"
                                   , String
    "TEXINDEXSTYLE"
                                   , String
    "TEXINPUTS"
                                   , String
    "TEXMFCNF"
                                   , String
    "TEXMFDBS"
                                   , String
    "TEXMFINI"
                                   , String
    "TEXMFSCRIPTS"
                                   , String
    "TEXMFVAR"
                                   , String
    "TEXPICTS"
                                   , String
    "TEXPKS"
                                   , String
    "TEXPOOL"
                                   , String
    "TEXPSHEADERS"
                                   , String
    "TEXSOURCES"
                                   , String
    "TEX_HUSH"
                                   , String
    "TFMFONTS"
                                   , String
    "TMPDIR"
                                   , String
    "TRFONTS"
                                   , String
    "TTFONTS"
                                   , String
    "USERPROFILE"
                                   , String
    "USE_TEXMFVAR"
                                   , String
    "USE_VARTEXFONTS"
                                   , String
    "VARTEXFONTS"
                                   , String
    "VFFONTS"
                                   , String
    "WEB2C"
                                   , String
    "WEBINPUTS"
                                   ]) Bool -> Bool -> Bool
    || String
    "TEXMF" String -> String -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` String
    e
      LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    MakePDFInfo Text
    "Relevant environment variables:"
                 (Text -> [Text] -> Text
    T.intercalate Text
    "\n" ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $ ((String, String) -> Text) -> [(String, String)] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (String, String) -> Text
    forall a. Show a => a -> Text
    tshow ([(String, String)] -> [Text]) -> [(String, String)] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ ((String, String) -> Bool)
    -> [(String, String)] -> [(String, String)]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (String -> Bool
    isRelevant (String -> Bool)
    -> ((String, String) -> String) -> (String, String) -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String, String) -> String
    forall a b. (a, b) -> a
    fst) [(String, String)]
    env)
      LogMessage -> m ()
    forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
    report (LogMessage -> m ()) -> LogMessage -> m ()
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> LogMessage
    MakePDFInfo Text
    "Source:" Text
    source

    handlePDFProgramNotFound :: String -> IE.IOError -> IO a
    handlePDFProgramNotFound :: forall a. String -> IOError -> IO a
    handlePDFProgramNotFound String
    program IOError
    e
      | IOError -> Bool
    IE.isDoesNotExistError IOError
    e =
          PandocError -> IO a
    forall e a. (HasCallStack, Exception e) => e -> IO a
    E.throwIO (PandocError -> IO a) -> PandocError -> IO a
    forall a b. (a -> b) -> a -> b
    $ Text -> PandocError
    PandocPDFProgramNotFoundError (Text -> PandocError) -> Text -> PandocError
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack String
    program
      | Bool
    otherwise = IOError -> IO a
    forall e a. (HasCallStack, Exception e) => e -> IO a
    E.throwIO IOError
    e

    utf8ToText :: ByteString -> Text
    utf8ToText :: ByteString -> Text
    utf8ToText ByteString
    lbs =
      case ByteString -> Either UnicodeException Text
    decodeUtf8' ByteString
    lbs of
        Left UnicodeException
    _  -> String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ ByteString -> String
    BC.unpack ByteString
    lbs  -- if decoding fails, treat as latin1
        Right Text
    t -> Text -> Text
    TL.toStrict Text
    t