never executed always true always false
    1 {-# LANGUAGE OverloadedStrings   #-}
    2 {-# LANGUAGE ScopedTypeVariables #-}
    3 {-|
    4 Copyright   : Written by David Himmelstrup
    5 License     : Unlicense
    6 Maintainer  : lemmih@gmail.com
    7 Stability   : experimental
    8 Portability : POSIX
    9 -}
   10 module Reanimate.LaTeX
   11   ( latex
   12   , latexWithHeaders
   13   , latexChunks
   14   , xelatex
   15   , xelatexWithHeaders
   16   , ctex
   17   , ctexWithHeaders
   18   , latexAlign
   19   )
   20 where
   21 
   22 import qualified Data.ByteString               as B
   23 import           Data.Text                                ( Text )
   24 import qualified Data.Text                     as T
   25 import qualified Data.Text.Encoding            as T
   26 import           Graphics.SvgTree                         ( Tree(..)
   27                                                           , parseSvgFile
   28                                                           )
   29 import           Reanimate.Cache
   30 import           Reanimate.Misc
   31 import           Reanimate.Svg
   32 import           Reanimate.Parameters
   33 import           System.FilePath                          ( replaceExtension
   34                                                           , takeFileName
   35                                                           , (</>)
   36                                                           )
   37 import           System.IO.Unsafe                         ( unsafePerformIO )
   38 
   39 -- | Invoke latex and import the result as an SVG object. SVG objects are
   40 --   cached to improve performance.
   41 --
   42 --   Example:
   43 --
   44 --   > latex "$e^{i\\pi}+1=0$"
   45 --
   46 --   <<docs/gifs/doc_latex.gif>>
   47 latex :: T.Text -> Tree
   48 latex = latexWithHeaders []
   49 
   50 -- | Invoke latex with extra script headers.
   51 latexWithHeaders :: [T.Text] -> T.Text -> Tree
   52 latexWithHeaders = someTexWithHeaders "latex" "dvi" []
   53 
   54 someTexWithHeaders :: String -> String -> [String] -> [T.Text] -> T.Text -> Tree
   55 someTexWithHeaders _exec _dvi _args _headers tex | pNoExternals = mkText tex
   56 someTexWithHeaders exec dvi args headers tex =
   57   (unsafePerformIO . (cacheMem . cacheDiskSvg) (latexToSVG dvi exec args))
   58     script
   59  where
   60   script = mkTexScript exec args headers tex
   61 
   62 -- | Invoke latex and separate results.
   63 latexChunks :: [T.Text] -> [Tree]
   64 latexChunks chunks | pNoExternals = map mkText chunks
   65 latexChunks chunks                = worker (svgGlyphs $ latex $ T.concat chunks) chunks
   66  where
   67   merge lst = mkGroup [ fmt svg | (fmt, _, svg) <- lst ]
   68   worker [] [] = []
   69   worker _ [] = error "latex chunk mismatch"
   70   worker everything (x : xs) =
   71     let width = length $ svgGlyphs (latex x)
   72     in merge (take width everything) : worker (drop width everything) xs
   73 
   74 -- | Invoke xelatex and import the result as an SVG object. SVG objects are
   75 --   cached to improve performance. Xelatex has support for non-western scripts.
   76 xelatex :: Text -> Tree
   77 xelatex = xelatexWithHeaders []
   78 
   79 -- | Invoke xelatex with extra script headers.
   80 xelatexWithHeaders :: [T.Text] -> T.Text -> Tree
   81 xelatexWithHeaders = someTexWithHeaders "xelatex" "xdv" ["-no-pdf"]
   82 
   83 -- | Invoke xelatex with "\usepackage[UTF8]{ctex}" and import the result as an
   84 --   SVG object. SVG objects are cached to improve performance. Xelatex has
   85 --   support for non-western scripts.
   86 --
   87 --   Example:
   88 --
   89 --   > ctex "中文"
   90 --
   91 --   <<docs/gifs/doc_ctex.gif>>
   92 ctex :: T.Text -> Tree
   93 ctex = ctexWithHeaders []
   94 
   95 -- | Invoke xelatex with extra script headers + ctex headers.
   96 ctexWithHeaders :: [T.Text] -> T.Text -> Tree
   97 ctexWithHeaders headers = xelatexWithHeaders ("\\usepackage[UTF8]{ctex}" : headers)
   98 
   99 -- | Invoke latex and import the result as an SVG object. SVG objects are
  100 --   cached to improve performance. This wraps the TeX code in an 'align*'
  101 --   context.
  102 --
  103 --   Example:
  104 --
  105 --   > latexAlign "R = \\frac{{\\Delta x}}{{kA}}"
  106 --
  107 --   <<docs/gifs/doc_latexAlign.gif>>
  108 latexAlign :: Text -> Tree
  109 latexAlign tex = latex $ T.unlines ["\\begin{align*}", tex, "\\end{align*}"]
  110 
  111 postprocess :: Tree -> Tree
  112 postprocess = simplify
  113 
  114 -- executable, arguments, header, tex
  115 latexToSVG :: String -> String -> [String] -> Text -> IO Tree
  116 latexToSVG dviExt latexExec latexArgs tex = do
  117   latexBin <- requireExecutable latexExec
  118   dvisvgm  <- requireExecutable "dvisvgm"
  119   withTempDir $ \tmp_dir -> withTempFile "tex" $ \tex_file ->
  120     withTempFile "svg" $ \svg_file -> do
  121       let dvi_file =
  122             tmp_dir </> replaceExtension (takeFileName tex_file) dviExt
  123       B.writeFile tex_file (T.encodeUtf8 tex)
  124       runCmd
  125         latexBin
  126         (  latexArgs
  127         ++ [ "-interaction=nonstopmode"
  128            , "-halt-on-error"
  129            , "-output-directory=" ++ tmp_dir
  130            , tex_file
  131            ]
  132         )
  133       runCmd
  134         dvisvgm
  135         [ dvi_file
  136         , "--precision=5"
  137         , "--exact"    -- better bboxes.
  138         , "--no-fonts" -- use glyphs instead of fonts.
  139         , "--scale=0.1,-0.1"
  140         , "--verbosity=0"
  141         , "-o"
  142         , svg_file
  143         ]
  144       svg_data <- B.readFile svg_file
  145       case parseSvgFile svg_file svg_data of
  146         Nothing  -> error "Malformed svg"
  147         Just svg -> return $ postprocess $ unbox $ replaceUses svg
  148 
  149 mkTexScript :: String -> [String] -> [Text] -> Text -> Text
  150 mkTexScript latexExec latexArgs texHeaders tex =
  151   T.unlines
  152     $  [ "% " <> T.pack (unwords (latexExec : latexArgs))
  153        , "\\documentclass[preview]{standalone}"
  154        , "\\usepackage{amsmath}"
  155        , "\\usepackage{gensymb}"
  156        ]
  157     ++ texHeaders
  158     ++ [ "\\usepackage[english]{babel}"
  159        , "\\linespread{1}"
  160        , "\\begin{document}"
  161        , tex
  162        , "\\end{document}"
  163        ]
  164 
  165 {- Packages used by manim.
  166 
  167 \\\usepackage{amsmath}\n\
  168 \\\usepackage{amssymb}\n\
  169 \\\usepackage{dsfont}\n\
  170 \\\usepackage{setspace}\n\
  171 \\\usepackage{relsize}\n\
  172 \\\usepackage{textcomp}\n\
  173 \\\usepackage{mathrsfs}\n\
  174 \\\usepackage{calligra}\n\
  175 \\\usepackage{wasysym}\n\
  176 \\\usepackage{ragged2e}\n\
  177 \\\usepackage{physics}\n\
  178 \\\usepackage{xcolor}\n\
  179 \\\usepackage{textcomp}\n\
  180 \\\usepackage{xfrac}\n\
  181 \\\usepackage{microtype}\n\
  182 -}