{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Reanimate.LaTeX
( latex
, latexWithHeaders
, latexChunks
, xelatex
, xelatexWithHeaders
, ctex
, ctexWithHeaders
, latexAlign
)
where
import qualified Data.ByteString as B
import Data.Text ( Text )
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Graphics.SvgTree ( Tree
, parseSvgFile
)
import Reanimate.Cache
import Reanimate.Misc
import Reanimate.Svg
import Reanimate.Parameters
import System.FilePath ( replaceExtension
, takeFileName
, (</>)
)
import System.IO.Unsafe ( unsafePerformIO )
latex :: T.Text -> Tree
latex = latexWithHeaders []
latexWithHeaders :: [T.Text] -> T.Text -> Tree
latexWithHeaders = someTexWithHeaders "latex" "dvi" []
someTexWithHeaders :: String -> String -> [String] -> [T.Text] -> T.Text -> Tree
someTexWithHeaders _exec _dvi _args _headers tex | pNoExternals = mkText tex
someTexWithHeaders exec dvi args headers tex =
(unsafePerformIO . (cacheMem . cacheDiskSvg) (latexToSVG dvi exec args))
script
where
script = mkTexScript exec args headers tex
latexChunks :: [T.Text] -> [Tree]
latexChunks chunks | pNoExternals = map mkText chunks
latexChunks chunks = worker (svgGlyphs $ latex $ T.concat chunks) chunks
where
merge lst = mkGroup [ fmt svg | (fmt, _, svg) <- lst ]
worker [] [] = []
worker _ [] = error "latex chunk mismatch"
worker everything (x : xs) =
let width = length $ svgGlyphs (latex x)
in merge (take width everything) : worker (drop width everything) xs
xelatex :: Text -> Tree
xelatex = xelatexWithHeaders []
xelatexWithHeaders :: [T.Text] -> T.Text -> Tree
xelatexWithHeaders = someTexWithHeaders "xelatex" "xdv" ["-no-pdf"]
ctex :: T.Text -> Tree
ctex = ctexWithHeaders []
ctexWithHeaders :: [T.Text] -> T.Text -> Tree
ctexWithHeaders headers = xelatexWithHeaders ("\\usepackage[UTF8]{ctex}" : headers)
latexAlign :: Text -> Tree
latexAlign tex = latex $ T.unlines ["\\begin{align*}", tex, "\\end{align*}"]
postprocess :: Tree -> Tree
postprocess = simplify
latexToSVG :: String -> String -> [String] -> Text -> IO Tree
latexToSVG dviExt latexExec latexArgs tex = do
latexBin <- requireExecutable latexExec
dvisvgm <- requireExecutable "dvisvgm"
withTempDir $ \tmp_dir -> withTempFile "tex" $ \tex_file ->
withTempFile "svg" $ \svg_file -> do
let dvi_file =
tmp_dir </> replaceExtension (takeFileName tex_file) dviExt
B.writeFile tex_file (T.encodeUtf8 tex)
runCmd
latexBin
( latexArgs
++ [ "-interaction=nonstopmode"
, "-halt-on-error"
, "-output-directory=" ++ tmp_dir
, tex_file
]
)
runCmd
dvisvgm
[ dvi_file
, "--precision=5"
, "--exact"
, "--no-fonts"
, "--scale=0.1,-0.1"
, "--verbosity=0"
, "-o"
, svg_file
]
svg_data <- B.readFile svg_file
case parseSvgFile svg_file svg_data of
Nothing -> error "Malformed svg"
Just svg -> return $ postprocess $ unbox $ replaceUses svg
mkTexScript :: String -> [String] -> [Text] -> Text -> Text
mkTexScript latexExec latexArgs texHeaders tex =
T.unlines
$ [ "% " <> T.pack (unwords (latexExec : latexArgs))
, "\\documentclass[preview]{standalone}"
, "\\usepackage{amsmath}"
, "\\usepackage{gensymb}"
]
++ texHeaders
++ [ "\\usepackage[english]{babel}"
, "\\linespread{1}"
, "\\begin{document}"
, tex
, "\\end{document}"
]