mathblog-0.6: src/MB/Processors/Tikz.hs
module MB.Processors.Tikz
( tikzProcessor
)
where
import Control.Monad.Trans
import Data.List
import Data.Digest.Pure.SHA
import Data.ByteString.Lazy.Char8 (pack)
import System.Process
import System.Directory
import System.Exit
import Codec.Picture
import qualified Text.Pandoc as Pandoc
import MB.Types
tikzProcessor :: Processor
tikzProcessor =
nullProcessor { preProcessPost = Just renderTikz
}
imgDimensions :: DynamicImage -> (Int, Int)
imgDimensions (ImageY8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageY16 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageYF img) = (imageWidth img, imageHeight img)
imgDimensions (ImageYA8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageYA16 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageRGB8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageRGB16 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageRGBF img) = (imageWidth img, imageHeight img)
imgDimensions (ImageRGBA8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageRGBA16 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageYCbCr8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageCMYK8 img) = (imageWidth img, imageHeight img)
imgDimensions (ImageCMYK16 img) = (imageWidth img, imageHeight img)
renderTikz :: Post -> BlogM Post
renderTikz post = do
let Pandoc.Pandoc m blocks = postAst post
newBlocks <- mapM (renderTikzScript post) blocks
return $ post { postAst = Pandoc.Pandoc m newBlocks }
renderTikzScript :: Post
-> Pandoc.Block
-> BlogM Pandoc.Block
renderTikzScript post blk@(Pandoc.CodeBlock ("tikz", classes, _) rawScript) = do
blog <- theBlog
let digestInput = postTeXMacros post ++ rawScript
-- Generate an image name in the images/ directory of the blog
-- data directory. Use a hash of the preamble name and script
-- contents so we can avoid rendering the image again if it
-- already exists.
hash = showDigest $ sha1 $ pack digestInput
imageFilename = "tikz-" ++ hash ++ ".png"
imagePath = ofsImagePath (outputFS blog) imageFilename
preamble = unlines [ "\\documentclass{article}"
, "\\usepackage{tikz}"
, "\\usetikzlibrary{intersections,backgrounds,fit,calc,positioning}"
, "\\usepackage{pgfplots}"
, "\\usepackage{amsmath}"
, "\\pgfrealjobname{tmp}"
-- Include any TeX macro content in the
-- document preamble so it can be used in the
-- TikZ markup.
, postTeXMacros post
, "\\begin{document}"
, "\\begin{figure}"
, "\\beginpgfgraphicnamed{testfigure}"
, "\\begin{tikzpicture}"
]
postamble = unlines [ "\\end{tikzpicture}"
, "\\endpgfgraphicnamed"
, "\\end{figure}"
, "\\end{document}"
]
latexSource = preamble ++ rawScript ++ postamble
let newBlock pth = do
imgResult <- readImage pth
case imgResult of
Left e -> do
putStrLn $ "Yikes! Error reading an image we generated (" ++ show pth ++ "): " ++ e
exitFailure
Right img -> do
let (w, h) = imgDimensions img
return $ Pandoc.Para [ Pandoc.RawInline (Pandoc.Format "html") $
concat [ "<img src=\"/generated-images/"
, imageFilename
, "\" width=\""
, show (w `div` 2)
, "\" height=\""
, show (h `div` 2)
, "\" class=\""
, intercalate " " classes
, "\">"
]
]
e <- liftIO $ doesFileExist imagePath
case e of
True -> liftIO $ newBlock imagePath
False -> do
liftIO $ writeFile "/tmp/tmp.tex" latexSource
(s1, out1, _) <- liftIO $ readProcessWithExitCode "pdflatex" [ "-halt-on-error", "-output-directory", "/tmp"
, "--jobname", "testfigure", "/tmp/tmp.tex"] ""
case s1 of
ExitFailure _ ->
liftIO $ do
putStrLn out1
putStrLn ">>>>>>> Input source >>>>>>>"
putStrLn latexSource
putStrLn ">>>>> End input source >>>>>"
return blk
ExitSuccess -> do
-- Convert the temporary file to a PNG.
(s2, out2, err) <- liftIO $ readProcessWithExitCode "convert" [ "-density", "250%"
, "-quality", "100"
, "/tmp/testfigure.pdf"
, imagePath
] ""
case s2 of
ExitFailure _ ->
liftIO $ do
putStrLn "Could not render Tikz picture:"
putStrLn "Equation was:"
putStrLn latexSource
putStrLn "dvipng output:"
putStrLn out2
putStrLn err
return blk
ExitSuccess -> liftIO $ newBlock imagePath
renderTikzScript _ b = return b