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 -}