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