smcdel-1.2.0: src/SMCDEL/Internal/TexDisplay.hs
{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses, AllowAmbiguousTypes, OverloadedStrings, BangPatterns #-}
module SMCDEL.Internal.TexDisplay where
import Control.Monad
import Data.List
import qualified Data.Text.Lazy as T
import System.Directory (findExecutable)
import System.IO (hGetContents)
import System.IO.Temp
import System.IO.Unsafe (unsafePerformIO)
import System.Process
import Data.GraphViz
import Data.GraphViz.Types.Generalised
import Data.GraphViz.Types.Monadic
import Data.Time.Clock.POSIX
begintab, endtab, newline :: String
begintab = "\\\\begin{tabular}{c}"
endtab = "\\\\end{tabular}"
newline = " \\\\\\\\[0pt] "
removeDoubleSpaces :: String -> String
removeDoubleSpaces (' ':' ':rest) = removeDoubleSpaces (' ':rest)
removeDoubleSpaces (x : xs ) = x : removeDoubleSpaces xs
removeDoubleSpaces [ ] = [ ]
class TexAble a where
tex :: a -> String
texTo :: a -> String -> IO ()
texTo !x filename = writeFile (filename++".tex") (tex x)
texDocumentTo :: a -> String -> IO ()
texDocumentTo !x filename =
writeFile (filename++".tex") (pre ++ tex x ++ post) where
pre = concat [ "\\documentclass{standalone}"
, "\\usepackage[utf8]{inputenc}"
, "\\usepackage{tikz,fontenc,graphicx}"
, "\\usepackage[pdftex]{hyperref}"
, "\\hypersetup{pdfborder={0 0 0},breaklinks=true}"
, "\\begin{document}"
]
post= "\\end{document}"
pdfTo :: a -> String -> IO ()
pdfTo !x filename = do
texDocumentTo x filename
runAndWait $ "cd " ++ filename ++ "/../; /usr/bin/pdflatex -interaction=nonstopmode "++filename++".tex"
disp :: a -> IO ()
disp !x = withSystemTempDirectory "smcdel" $ \tmpdir -> do
ts <- round <$> getPOSIXTime
let filename = tmpdir ++ "/disp-" ++ show (ts :: Int)
pdfTo x filename
runIgnoreAndWait $ "/usr/bin/okular " ++ filename ++ ".pdf"
svgViaTex :: a -> String
svgViaTex !x = unsafePerformIO $ withSystemTempDirectory "smcdel" $ \tmpdir -> do
ts <- round <$> getPOSIXTime
let filename = tmpdir ++ "/svgViaTex-" ++ show (ts :: Int)
pdfTo x filename
runAndWait $ "pdftocairo -nocrop -svg "++filename++".pdf "++filename++".svg"
readFile (filename ++ ".svg")
instance TexAble String where
tex i = " \\text{" ++ i ++ "} "
instance TexAble Int where
tex = show
-- | TeXing assignments as sets
instance TexAble a => TexAble [(a,Bool)] where
tex ass = case filter snd ass of
[] -> ""
ps -> "$" ++ intercalate "," (map (tex.fst) ps) ++ "$"
runAndWait :: String -> IO ()
runAndWait command = do
(_inp,_out,err,pid) <- runInteractiveCommand command
_ <- waitForProcess pid
hGetContents err >>= (\x -> unless (null x) (putStrLn x))
return ()
runIgnoreAndWait :: String -> IO ()
runIgnoreAndWait command = do
(_inp,_out,_err,pid) <- runInteractiveCommand command
_ <- waitForProcess pid
return ()
class KripkeLike a where
getNodes :: a -> [(String,String)] -- nid,nlabel
getEdges :: a -> [(String,String,String)] -- elabel,from,to
getActuals :: a -> [String] -- nid
getActuals = const []
directed :: a -> Bool
directed = const True
nodeAts :: a -> Bool -> Attributes
nodeAts _ True = [shape DoubleCircle]
nodeAts _ False = [shape Circle]
toGraph :: a -> Data.GraphViz.Types.Generalised.DotGraph String
toGraph x = (if directed x then digraph' else graph') $ do
let nodes = getNodes x
let actuals = filter (\n -> fst n `elem` getActuals x) nodes
mapM_
(\(nid,nlabel) -> node nid (toLabel nlabel : nodeAts x True))
actuals
mapM_
(\(nid,nlabel) -> node nid (toLabel nlabel : nodeAts x False))
(nodes \\ actuals)
mapM_
(\(elabel,from,to) -> edge from to [toLabel elabel])
(getEdges x)
dispDot :: a -> IO ()
dispDot x = runGraphvizCanvas Dot (toGraph x) Xlib
textDot :: a -> T.Text
textDot = T.intercalate " " . T.lines . printDotGraph . toGraph
svg :: a -> String
svg x = unsafePerformIO $
withSystemTempDirectory "smcdel" $ \tmpdir -> do
_ <- runGraphvizCommand Dot (toGraph x) Svg (tmpdir ++ "/temp.svg")
readFile (tmpdir ++ "/temp.svg")
newtype ViaDot a = ViaDot a
deriving (Eq,Ord,Show)
dot2tex :: String -> IO ()
dot2tex args = do
haveDot2tex <- findExecutable "dot2tex"
case haveDot2tex of
Nothing -> error "Please install dot2tex which is needed to show this."
Just d2t -> runAndWait $ d2t ++ args
dot2texDefaultArgs :: String
dot2texDefaultArgs = " -ftikz -traw -p --autosize -w --usepdflatex "
instance (Ord a, Show a, KripkeLike a) => TexAble (ViaDot a) where
tex (ViaDot x) = unsafePerformIO $
withSystemTempDirectory "smcdel" $ \tmpdir -> do
_ <- runGraphviz (toGraph x) DotOutput (tmpdir ++ "/temp.dot")
dot2tex $ dot2texDefaultArgs ++ " --figonly " ++ tmpdir ++ "/temp.dot | sed '/^$/d' > " ++ tmpdir ++ "/temp.tex;"
readFile (tmpdir ++ "/temp.tex") -- FIXME: avoid this by using runInteractiveCommand and buffers
texTo (ViaDot x) filename = do
_ <- runGraphviz (toGraph x) DotOutput (filename ++ ".dot")
dot2tex $ dot2texDefaultArgs ++ " --figonly " ++ filename ++ ".dot | sed '/^$/d' > " ++ filename ++ ".tex;"
texDocumentTo (ViaDot x) filename = do
_ <- runGraphviz (toGraph x) DotOutput (filename ++ ".dot")
dot2tex $ dot2texDefaultArgs ++ filename ++ ".dot -o " ++ filename ++ ".tex;"