packages feed

mathblog-0.3: src/MB/Tikz.hs

module MB.Tikz
    ( tikzProcessor
    )
where

import Data.List
    ( intercalate
    )
import Data.Digest.Pure.SHA
    ( showDigest
    , sha1
    )
import Data.ByteString.Lazy.Char8
    ( pack
    )
import System.Process
    ( readProcessWithExitCode
    )
import System.Exit
    ( ExitCode(..)
    )
import qualified Text.Pandoc as Pandoc
import MB.Types
import qualified MB.Files as Files

tikzProcessor :: Processor
tikzProcessor =
    nullProcessor { preProcessPost = Just renderTikz
                  }

renderTikz :: Blog -> Post -> IO Post
renderTikz config post = do
  let Pandoc.Pandoc m blocks = postAst post
  newBlocks <- mapM (renderTikzScript config) blocks
  return $ post { postAst = Pandoc.Pandoc m newBlocks }

renderTikzScript :: Blog
                 -> Pandoc.Block
                 -> IO Pandoc.Block
renderTikzScript config blk@(Pandoc.CodeBlock ("tikz", classes, _) rawScript) = do
  putStrLn "Rendering equation graph (tikz)"

  let digestInput = 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 = Files.imageFilename config imageFilename
      preamble = unlines [ "\\documentclass{article}"
                         , "\\usepackage{tikz}"
                         , "\\usepackage{pgfplots}"
                         , "\\pgfrealjobname{tmp}"

                         , "\\begin{document}"
                         , "\\begin{figure}"
                         , "\\beginpgfgraphicnamed{testfigure}"
                         , "\\begin{tikzpicture}"
                         ]
      postamble = unlines [ "\\end{tikzpicture}"
                          , "\\endpgfgraphicnamed"
                          , "\\end{figure}"
                          , "\\end{document}"
                          ]

      latexSource = preamble ++ rawScript ++ postamble

  writeFile "/tmp/tmp.tex" latexSource

  (s1, out1, _) <- readProcessWithExitCode "pdflatex" [ "-output-directory", "/tmp"
                                                      , "--jobname", "testfigure", "/tmp/tmp.tex"] ""
  case s1 of
    ExitFailure _ -> putStrLn out1 >> return blk
    ExitSuccess -> do
            -- Convert the temporary file to a PNG.
            (s2, out2, err) <- readProcessWithExitCode "convert" [ "-density", "120"
                                                                 , "-quality", "100"
                                                                 , "/tmp/testfigure.pdf"
                                                                 , imagePath
                                                                 ] ""

            case s2 of
              ExitFailure _ -> do
                      putStrLn "Could not render Tikz picture:"
                      putStrLn "Equation was:"
                      putStrLn latexSource
                      putStrLn "dvipng output:"
                      putStrLn out2
                      putStrLn err
                      return blk
              ExitSuccess ->
                  return $ Pandoc.Para [Pandoc.RawInline "html" $
                                              concat [ "<img src=\"/generated-images/"
                                                     , imageFilename
                                                     , "\" class=\""
                                                     , intercalate " " classes
                                                     , "\">"
                                                     ]
                                       ]
renderTikzScript _ b = return b