{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
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 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)
import Data.Digest.Pure.SHA (sha1, showDigest)
#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 =
intercalate "/" . map (filter (/='\\')) . splitDirectories
#endif
makePDF :: (PandocMonad m, MonadIO m, MonadMask m)
=> String
-> [String]
-> (WriterOptions -> Pandoc -> m Text)
-> WriterOptions
-> Pandoc
-> 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]
Text
source <- WriterOptions -> Pandoc -> m Text
writer WriterOptions
opts Pandoc
doc
Verbosity
verbosity <- m Verbosity
forall (m :: * -> *). PandocMonad m => m Verbosity
getVerbosity
IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either ByteString ByteString)
-> m (Either ByteString ByteString))
-> IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ Verbosity
-> String
-> [String]
-> (String -> [String])
-> Text
-> IO (Either ByteString ByteString)
toPdfViaTempFile Verbosity
verbosity String
program [String]
pdfargs String -> [String]
forall {a}. IsString a => a -> [a]
mkOutArgs Text
source
String
"typst" -> do
Text
source <- WriterOptions -> Pandoc -> m Text
writer WriterOptions
opts Pandoc
doc
Verbosity
verbosity <- m Verbosity
forall (m :: * -> *). PandocMonad m => m Verbosity
getVerbosity
IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either ByteString ByteString)
-> m (Either ByteString ByteString))
-> IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$
Verbosity
-> String
-> [String]
-> (String -> [String])
-> Text
-> IO (Either ByteString ByteString)
toPdfViaTempFile Verbosity
verbosity String
program (String
"compile"String -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
pdfargs) (String -> [String] -> [String]
forall a. a -> [a] -> [a]
:[]) Text
source
String
"pdfroff" -> do
Text
source <- WriterOptions -> Pandoc -> m Text
writer WriterOptions
opts Pandoc
doc
let paperargs :: [String]
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]
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
String -> [String] -> Text -> m (Either ByteString ByteString)
forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
String -> [String] -> Text -> m (Either ByteString ByteString)
generic2pdf String
program [String]
args Text
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
let tmpdir = changePathSeparators tmpdir'
#else
let tmpdir :: String
tmpdir = String
tmpdir'
#endif
Pandoc
doc' <- WriterOptions -> String -> Pandoc -> m Pandoc
forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
WriterOptions -> String -> Pandoc -> m Pandoc
handleImages WriterOptions
opts String
tmpdir Pandoc
doc
Text
source <- WriterOptions -> Pandoc -> m Text
writer WriterOptions
opts{ writerExtensions =
disableExtension Ext_smart
(writerExtensions opts) } Pandoc
doc'
case String
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
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
String
tmp <- IO String -> m String
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO String
getTemporaryDirectory
Maybe String
uname <- IO (Maybe String) -> m (Maybe String)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe String) -> m (Maybe String))
-> IO (Maybe String) -> m (Maybe String)
forall a b. (a -> b) -> a -> b
$ IO (Maybe String)
-> (SomeException -> IO (Maybe String)) -> IO (Maybe String)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
E.catch
(do (ExitCode
ec, String
sout, String
_) <- String -> [String] -> String -> IO (ExitCode, String, String)
readProcessWithExitCode String
"uname" [String
"-o"] String
""
if ExitCode
ec ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
then Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) String
sout
else 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)
(\(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 Char
'~' Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
tmp Bool -> Bool -> Bool
|| Maybe String
uname Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just String
"Cygwin"
then String -> String -> (String -> m a) -> m a
forall (m :: * -> *) a.
(MonadMask m, MonadIO m) =>
String -> String -> (String -> m a) -> m a
withTempDirectory String
"." String
templ String -> m a
action
else String -> (String -> m a) -> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
templ String -> m a
action
makeWithWkhtmltopdf :: (PandocMonad m, MonadIO m)
=> String
-> [String]
-> (WriterOptions -> Pandoc -> m Text)
-> WriterOptions
-> Pandoc
-> 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
MathJax Text
_ -> [String
"--run-script", String
"MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });",
String
"--window-status", String
"mathjax_loaded"]
HTMLMathMethod
_ -> []
Context Text
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, Maybe Text) -> [String]
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]
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)
Text
source <- WriterOptions -> Pandoc -> m Text
writer WriterOptions
opts Pandoc
doc
Verbosity
verbosity <- m Verbosity
forall (m :: * -> *). PandocMonad m => m Verbosity
getVerbosity
IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either ByteString ByteString)
-> m (Either ByteString ByteString))
-> IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ Verbosity
-> String
-> [String]
-> (String -> [String])
-> Text
-> IO (Either ByteString ByteString)
toPdfViaTempFile Verbosity
verbosity String
program [String]
args (String -> [String] -> [String]
forall a. a -> [a] -> [a]
:[]) Text
source
handleImages :: (PandocMonad m, MonadIO m)
=> WriterOptions
-> FilePath
-> Pandoc
-> 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
Either Text String
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
Text
newPath <-
case Either Text String
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
Inline -> m Inline
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Attr -> [Inline] -> (Text, Text) -> Inline
Image Attr
attr [Inline]
ils (Text
newPath, Text
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
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
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
(ExitCode
exit, ByteString
_) <- 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 ExitCode
exit ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
then 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
$ String -> Either Text String
forall a b. b -> Either a b
Right String
pdfOut
else 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
"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 SHA1State -> String
forall t. Digest t -> String
showDigest (ByteString -> Digest SHA1State
sha1 (String -> ByteString
UTF8.fromStringLazy 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
-> [String]
-> FilePath
-> Text
-> 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
(ExitCode
exit, ByteString
log', Maybe ByteString
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 (ExitCode
exit, Maybe ByteString
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
-> [String]
-> FilePath
-> Text
-> 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"
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
(ExitCode
exit, ByteString
log', Maybe ByteString
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 (ExitCode
exit, Maybe ByteString
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
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)
extractMsg :: ByteString -> ByteString
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
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'
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
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
"-"]
[(String, 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
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
forall (m :: * -> *).
PandocMonad m =>
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
showVerboseInfo (String -> Maybe String
forall a. a -> Maybe a
Just String
tmpDir) String
program [String]
programArgs [(String, String)]
env (ByteString -> Text
utf8ToText ByteString
sourceBL)
(ExitCode
exit, ByteString
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
sourceBL)
(String -> IOError -> IO (ExitCode, ByteString)
forall a. String -> IOError -> IO a
handlePDFProgramNotFound String
program)
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
"tectonic output" (ByteString -> Text
UTF8.toText (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.toStrict ByteString
out)
let pdfFile :: String
pdfFile = String
tmpDir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/texput.pdf"
(Maybe ByteString
_, Maybe ByteString
pdf) <- Maybe String -> String -> m (Maybe ByteString, Maybe ByteString)
forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
Maybe String -> String -> m (Maybe ByteString, Maybe ByteString)
getResultingPDF Maybe String
forall a. Maybe a
Nothing String
pdfFile
(ExitCode, ByteString, Maybe ByteString)
-> m (ExitCode, ByteString, Maybe ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ExitCode
exit, ByteString
out, Maybe ByteString
pdf)
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
Bool
pdfExists <- String -> m Bool
forall (m :: * -> *). PandocMonad m => String -> m Bool
fileExists String
pdfFile
Maybe ByteString
pdf <- if Bool
pdfExists
then (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (ByteString -> ByteString) -> ByteString -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
BL.fromChunks ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:[])) (ByteString -> Maybe ByteString)
-> m ByteString -> m (Maybe ByteString)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`
(String -> m ByteString
forall (m :: * -> *). PandocMonad m => String -> m ByteString
readFileStrict String
pdfFile)
else 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
Maybe ByteString
log' <- case Maybe String
logFile of
Just String
logFile' -> do
Bool
logExists <- String -> m Bool
forall (m :: * -> *). PandocMonad m => String -> m Bool
fileExists String
logFile'
if Bool
logExists
then ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (ByteString -> ByteString) -> ByteString -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
BL.fromStrict (ByteString -> Maybe ByteString)
-> m ByteString -> m (Maybe ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> m ByteString
forall (m :: * -> *). PandocMonad m => String -> m ByteString
readFileStrict String
logFile'
else 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
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
(Maybe ByteString, Maybe ByteString)
-> m (Maybe ByteString, Maybe ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe ByteString
log', Maybe ByteString
pdf)
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]
[(String, 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
let sep :: String
sep = [Char
searchPathSeparator]
let texinputs :: String
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, String)]
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"]
IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO Text
UTF8.readFile String
file) m Text -> (Text -> m ()) -> m ()
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
forall (m :: * -> *).
PandocMonad m =>
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
showVerboseInfo (String -> Maybe String
forall a. a -> Maybe a
Just String
tmpDir) String
program [String]
programArgs [(String, String)]
env''
[(String, String)]
-> [String]
-> Int
-> Maybe (Digest SHA1State)
-> m (ExitCode, ByteString, Maybe ByteString)
forall {m :: * -> *} {t}.
(PandocMonad m, Show t, MonadIO m, Ord t, Num t) =>
[(String, String)]
-> [String]
-> t
-> Maybe (Digest SHA1State)
-> m (ExitCode, ByteString, Maybe ByteString)
go [(String, String)]
env'' [String]
programArgs (Int
1 :: Int) Maybe (Digest SHA1State)
forall a. Maybe a
Nothing
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
-> Maybe (Digest SHA1State)
-> m (ExitCode, ByteString, Maybe ByteString)
go [(String, String)]
env'' [String]
programArgs t
runNumber Maybe (Digest SHA1State)
oldTocHash = do
let maxruns :: t
maxruns = t
4
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
(ExitCode
exit, ByteString
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)
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 output" (ByteString -> Text
UTF8.toText (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.toStrict ByteString
out)
let logFile :: String
logFile = String -> String -> String
replaceExtension String
file String
".log"
Bool
logExists <- String -> m Bool
forall (m :: * -> *). PandocMonad m => String -> m Bool
fileExists String
logFile
ByteString
logContents <- if Bool
logExists
then ByteString -> ByteString
BL.fromStrict (ByteString -> ByteString) -> m ByteString -> m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> m ByteString
forall (m :: * -> *). PandocMonad m => String -> m ByteString
readFileStrict String
logFile
else ByteString -> m ByteString
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
forall a. Monoid a => a
mempty
let rerunWarnings :: [ByteString]
rerunWarnings = ByteString -> [ByteString]
checkForRerun ByteString
logContents
Maybe (Digest SHA1State)
tocHash <- do
let tocFile :: String
tocFile = String -> String -> String
replaceExtension String
file String
".toc"
Bool
tocFileExists <- String -> m Bool
forall (m :: * -> *). PandocMonad m => String -> m Bool
fileExists String
tocFile
if Bool
tocFileExists
then do
ByteString
tocContents <- ByteString -> ByteString
BL.fromStrict (ByteString -> ByteString) -> m ByteString -> m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> m ByteString
forall (m :: * -> *). PandocMonad m => String -> m ByteString
readFileStrict String
tocFile
Maybe (Digest SHA1State) -> m (Maybe (Digest SHA1State))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Digest SHA1State) -> m (Maybe (Digest SHA1State)))
-> Maybe (Digest SHA1State) -> m (Maybe (Digest SHA1State))
forall a b. (a -> b) -> a -> b
$ Digest SHA1State -> Maybe (Digest SHA1State)
forall a. a -> Maybe a
Just (Digest SHA1State -> Maybe (Digest SHA1State))
-> Digest SHA1State -> Maybe (Digest SHA1State)
forall a b. (a -> b) -> a -> b
$! ByteString -> Digest SHA1State
sha1 ByteString
tocContents
else Maybe (Digest SHA1State) -> m (Maybe (Digest SHA1State))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Digest SHA1State)
forall a. Maybe a
Nothing
let rerunWarnings' :: [ByteString]
rerunWarnings' = [ByteString]
rerunWarnings [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++
[ByteString
"TOC changed" | Maybe (Digest SHA1State)
tocHash Maybe (Digest SHA1State) -> Maybe (Digest SHA1State) -> Bool
forall a. Eq a => a -> a -> Bool
/= Maybe (Digest SHA1State)
oldTocHash ]
if Bool -> Bool
not ([ByteString] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ByteString]
rerunWarnings') Bool -> Bool -> Bool
&& t
runNumber t -> t -> Bool
forall a. Ord a => a -> a -> Bool
< t
maxruns
then do
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
"Rerun needed"
(Text -> [Text] -> Text
T.intercalate Text
"\n"
((ByteString -> Text) -> [ByteString] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Text
UTF8.toText (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
BC.toStrict) [ByteString]
rerunWarnings'))
[(String, String)]
-> [String]
-> t
-> Maybe (Digest SHA1State)
-> m (ExitCode, ByteString, Maybe ByteString)
go [(String, String)]
env'' [String]
programArgs (t
runNumber t -> t -> t
forall a. Num a => a -> a -> a
+ t
1) Maybe (Digest SHA1State)
tocHash
else do
(Maybe ByteString
log', Maybe ByteString
pdf) <- Maybe String -> String -> m (Maybe ByteString, Maybe ByteString)
forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
Maybe String -> String -> m (Maybe ByteString, Maybe ByteString)
getResultingPDF (String -> Maybe String
forall a. a -> Maybe a
Just String
logFile) String
outfile
(ExitCode, ByteString, Maybe ByteString)
-> m (ExitCode, ByteString, Maybe ByteString)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ExitCode
exit, ByteString -> Maybe ByteString -> ByteString
forall a. a -> Maybe a -> a
fromMaybe ByteString
out Maybe ByteString
log', Maybe ByteString
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
[(String, 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
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
forall (m :: * -> *).
PandocMonad m =>
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
showVerboseInfo Maybe String
forall a. Maybe a
Nothing String
program [String]
args [(String, String)]
env' Text
source
(ExitCode
exit, ByteString
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]
args
(ByteString -> ByteString
BL.fromStrict (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
UTF8.fromText Text
source))
(String -> IOError -> IO (ExitCode, ByteString)
forall a. String -> IOError -> IO a
handlePDFProgramNotFound String
program)
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
$ case ExitCode
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
-> String
-> [String]
-> (String -> [String])
-> Text
-> IO (Either ByteString ByteString)
toPdfViaTempFile :: Verbosity
-> String
-> [String]
-> (String -> [String])
-> Text
-> IO (Either ByteString ByteString)
toPdfViaTempFile Verbosity
verbosity String
program [String]
args String -> [String]
mkOutArgs 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.html" ((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
[(String, String)]
env' <- IO [(String, String)]
getEnvironment
Text
fileContents <- String -> IO Text
UTF8.readFile String
file
PandocIO () -> IO ()
forall a. PandocIO a -> IO a
runIOorExplode (PandocIO () -> IO ()) -> PandocIO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Verbosity -> PandocIO ()
forall (m :: * -> *). PandocMonad m => Verbosity -> m ()
setVerbosity Verbosity
verbosity
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> PandocIO ()
forall (m :: * -> *).
PandocMonad m =>
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
showVerboseInfo Maybe String
forall a. Maybe a
Nothing String
program [String]
programArgs [(String, String)]
env' Text
fileContents
(ExitCode
exit, ByteString
out) <- 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)
PandocIO () -> IO ()
forall a. PandocIO a -> IO a
runIOorExplode (PandocIO () -> IO ()) -> PandocIO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Verbosity -> PandocIO ()
forall (m :: * -> *). PandocMonad m => Verbosity -> m ()
setVerbosity Verbosity
verbosity
LogMessage -> PandocIO ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> PandocIO ()) -> LogMessage -> PandocIO ()
forall a b. (a -> b) -> a -> b
$ Text -> Text -> LogMessage
MakePDFInfo Text
"pdf-engine output" (ByteString -> Text
UTF8.toText (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.toStrict ByteString
out)
Bool
pdfExists <- String -> IO Bool
doesFileExist String
pdfFile
Maybe ByteString
mbPdf <- if Bool
pdfExists
then ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (ByteString -> ByteString) -> ByteString -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
BL.fromChunks ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:[]) (ByteString -> Maybe ByteString)
-> IO ByteString -> IO (Maybe ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO ByteString
BS.readFile String
pdfFile
else Maybe ByteString -> IO (Maybe ByteString)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe ByteString
forall a. Maybe a
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
$ case (ExitCode
exit, Maybe ByteString
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
-> [String]
-> FilePath
-> Text
-> 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]
[(String, 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
Verbosity
verbosity <- m Verbosity
forall (m :: * -> *). PandocMonad m => m Verbosity
getVerbosity
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
tmpDir String -> String -> String
</> String
file) (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
UTF8.fromText Text
source
IO Text -> m Text
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO Text
UTF8.readFile (String
tmpDir String -> String -> String
</> String
file)) m Text -> (Text -> m ()) -> m ()
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
forall (m :: * -> *).
PandocMonad m =>
Maybe String
-> String -> [String] -> [(String, String)] -> Text -> m ()
showVerboseInfo (String -> Maybe String
forall a. a -> Maybe a
Just String
tmpDir) String
program [String]
programArgs [(String, String)]
env'
IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either ByteString ByteString)
-> m (Either ByteString ByteString))
-> IO (Either ByteString ByteString)
-> m (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ String
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
forall a. String -> IO a -> IO a
inDirectory String
tmpDir (IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString))
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ do
(ExitCode
exit, ByteString
out) <- 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)
PandocIO () -> IO ()
forall a. PandocIO a -> IO a
runIOorExplode (PandocIO () -> IO ()) -> PandocIO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Verbosity -> PandocIO ()
forall (m :: * -> *). PandocMonad m => Verbosity -> m ()
setVerbosity Verbosity
verbosity
LogMessage -> PandocIO ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> PandocIO ()) -> LogMessage -> PandocIO ()
forall a b. (a -> b) -> a -> b
$ Text -> Text -> LogMessage
MakePDFInfo Text
"ConTeXt run output" (ByteString -> Text
UTF8.toText (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.toStrict ByteString
out)
let pdfFile :: String
pdfFile = String -> String -> String
replaceExtension String
file String
".pdf"
Bool
pdfExists <- String -> IO Bool
doesFileExist String
pdfFile
Maybe ByteString
mbPdf <- if Bool
pdfExists
then (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (ByteString -> ByteString) -> ByteString -> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
BL.fromChunks ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:[])) (ByteString -> Maybe ByteString)
-> IO ByteString -> IO (Maybe ByteString)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` String -> IO ByteString
BS.readFile String
pdfFile
else Maybe ByteString -> IO (Maybe ByteString)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe ByteString
forall a. Maybe a
Nothing
case (ExitCode
exit, Maybe ByteString
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)))
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. 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. 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
Right Text
t -> Text -> Text
TL.toStrict Text
t