module Image.LaTeX.Render.Pandoc
(
convertFormulaDataURI
, convertAllFormulaeDataURI
, convertFormulaFiles
, convertAllFormulaeFiles
, NameSupply
, newNameSupply
, PandocFormulaOptions(..)
, ShrinkSize
, defaultPandocFormulaOptions
, hideError
, displayError
, convertFormulaDataURIWith
, convertAllFormulaeDataURIWith
, convertFormulaFilesWith
, convertAllFormulaeFilesWith
) where
import Text.Pandoc.Definition
import Text.Pandoc.Walk
import Image.LaTeX.Render
import Codec.Picture
import Control.Applicative
import Data.IORef
import System.FilePath
import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.ByteString.Lazy.Char8 as BS
dims :: Image a -> (Int,Int)
dims = liftA2 (,) imageWidth imageHeight
dimensions :: DynamicImage -> (Int,Int)
dimensions (ImageRGB8 i) = dims i
dimensions (ImageRGBA8 i) = dims i
dimensions (ImageRGB16 i) = dims i
dimensions (ImageRGBA16 i) = dims i
dimensions (ImageY8 i) = dims i
dimensions (ImageY16 i) = dims i
dimensions (ImageYA8 i) = dims i
dimensions (ImageYA16 i) = dims i
dimensions _ = error "Unsupported image format somehow!"
data PandocFormulaOptions = PandocFormulaOptions
{ shrinkBy :: ShrinkSize
, errorDisplay :: RenderError -> Inline
, formulaOptions :: MathType -> FormulaOptions
}
defaultPandocFormulaOptions :: PandocFormulaOptions
defaultPandocFormulaOptions = PandocFormulaOptions
{ shrinkBy = 2
, errorDisplay = displayError
, formulaOptions = \case DisplayMath -> displaymath; _ -> math
}
type ShrinkSize = Int
convertFormulaDataURI
:: EnvironmentOptions
-> PandocFormulaOptions
-> Inline -> IO Inline
convertFormulaDataURI = convertFormulaDataURIWith . imageForFormula
convertAllFormulaeDataURI
:: EnvironmentOptions
-> PandocFormulaOptions
-> Pandoc -> IO Pandoc
convertAllFormulaeDataURI e = walkM . convertFormulaDataURI e
convertFormulaDataURIWith
:: (FormulaOptions -> Formula -> IO (Either RenderError (Baseline, DynamicImage)))
-> PandocFormulaOptions
-> Inline -> IO Inline
convertFormulaDataURIWith f o (Math t s) = f (formulaOptions o t) s >>= \case
Left e -> return $ errorDisplay o e
Right (b,i) -> let
Right bs = encodeDynamicPng i
dataUri = "data:image/png;base64," ++ BS.unpack (B64.encode bs)
(ow,oh) = dimensions i
(w,h) = (ow `div` shrinkBy o, oh `div` shrinkBy o)
in return $ RawInline (Format "html") $
"<img width=" ++ show w ++
" alt=\"" ++ processAltString s ++ "\"" ++
" height=" ++ show h ++
" src=\"" ++ dataUri ++ "\"" ++
" class=" ++ (case t of InlineMath -> "inline-math"; _ -> "display-math") ++
" style=\"margin:0; vertical-align:-" ++ show (b `div` shrinkBy o) ++ "px;\"/>"
where processAltString = (>>= \case
'<' -> "<"
'>' -> ">"
'&' -> "&"
'"' -> """
'\'' -> "&39;"
'\n' -> " "
'\r' -> " "
'\t' -> " "
x -> [x])
convertFormulaDataURIWith _ _ x = return x
convertAllFormulaeDataURIWith
:: (FormulaOptions -> Formula -> IO (Either RenderError (Baseline, DynamicImage)))
-> PandocFormulaOptions
-> Pandoc -> IO Pandoc
convertAllFormulaeDataURIWith f = walkM . convertFormulaDataURIWith f
type NameSupply = IORef Int
newNameSupply :: IO NameSupply
newNameSupply = newIORef 0
convertFormulaFilesWith
:: (FormulaOptions -> Formula -> IO (Either RenderError (Baseline, DynamicImage)))
-> NameSupply
-> FilePath
-> PandocFormulaOptions
-> Inline -> IO Inline
convertFormulaFilesWith f ns bn o (Math t s) = f (formulaOptions o t) s >>= \case
Left e -> return $ errorDisplay o e
Right (b,i) -> do
fn <- readIORef ns
modifyIORef ns (+1)
let uri = bn </> show fn <.> "png"
(ow,oh) = dimensions i
(w,h) = (ow `div` shrinkBy o, oh `div` shrinkBy o)
_ <- writeDynamicPng uri i
return $ RawInline (Format "html") $
"<img width=" ++ show w ++
" height=" ++ show h ++
" src=\"" ++ uri ++ "\"" ++
" class=" ++ (case t of InlineMath -> "inline-math"; _ -> "display-math") ++
" style=\"margin:0; vertical-align:-" ++ show (b `div` shrinkBy o) ++ "px;\"/>"
convertFormulaFilesWith _ _ _ _ x = return x
convertFormulaFiles
:: EnvironmentOptions
-> NameSupply
-> FilePath
-> PandocFormulaOptions
-> Inline -> IO Inline
convertFormulaFiles = convertFormulaFilesWith . imageForFormula
convertAllFormulaeFiles
:: EnvironmentOptions
-> NameSupply
-> FilePath
-> PandocFormulaOptions
-> Pandoc -> IO Pandoc
convertAllFormulaeFiles eo ns fp = walkM . convertFormulaFiles eo ns fp
convertAllFormulaeFilesWith
:: (FormulaOptions -> Formula -> IO (Either RenderError (Baseline, DynamicImage)))
-> NameSupply
-> FilePath
-> PandocFormulaOptions
-> Pandoc -> IO Pandoc
convertAllFormulaeFilesWith x y a = walkM . convertFormulaFilesWith x y a
hideError :: RenderError -> Inline
hideError = const $ Str blank
where
blank = "Error"
displayError :: RenderError -> Inline
displayError ImageIsEmpty = pandocError [Str "The rendered image was empty"]
displayError CannotDetectBaseline = pandocError [Str "Cannot detect baseline in rendered image"]
displayError (LaTeXFailure str) = pandocError [Str "LaTeX failed:", LineBreak, Code nullAttr str]
displayError (DVIPSFailure str) = pandocError [Str "DVIPS failed:", LineBreak, Code nullAttr str]
displayError (IMConvertFailure str) = pandocError [Str "convert failed:", LineBreak, Code nullAttr str]
displayError (ImageReadError str) = pandocError [Str "Error reading image:", LineBreak, Code nullAttr str]
displayError (IOException e) = pandocError [Str "IO Exception:", LineBreak, Code nullAttr $ show e]
pandocError :: [Inline] -> Inline
pandocError = Strong . (Emph [Str "Error:"] :)