##// END OF EJS Templates
Use batchmode in latex_to_png_dvipng...
Thomas Kluyver -
Show More
@@ -1,251 +1,251 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for handling LaTeX.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2010 IPython Development Team.
10 10 #
11 11 # Distributed under the terms of the Modified BSD License.
12 12 #
13 13 # The full license is in the file COPYING.txt, distributed with this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from io import BytesIO
21 21 from base64 import encodestring
22 22 import os
23 23 import tempfile
24 24 import shutil
25 25 import subprocess
26 26
27 27 from IPython.utils.process import find_cmd, FindCmdError
28 28 from IPython.config.configurable import SingletonConfigurable
29 29 from IPython.utils.traitlets import List, CBool, CUnicode
30 30 from IPython.utils.py3compat import bytes_to_str
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Tools
34 34 #-----------------------------------------------------------------------------
35 35
36 36
37 37 class LaTeXTool(SingletonConfigurable):
38 38 """An object to store configuration of the LaTeX tool."""
39 39
40 40 backends = List(
41 41 CUnicode, ["matplotlib", "dvipng"],
42 42 help="Preferred backend to draw LaTeX math equations. "
43 43 "Backends in the list are checked one by one and the first "
44 44 "usable one is used. Note that `matplotlib` backend "
45 45 "is usable only for inline style equations. To draw "
46 46 "display style equations, `dvipng` backend must be specified. ",
47 47 # It is a List instead of Enum, to make configuration more
48 48 # flexible. For example, to use matplotlib mainly but dvipng
49 49 # for display style, the default ["matplotlib", "dvipng"] can
50 50 # be used. To NOT use dvipng so that other repr such as
51 51 # unicode pretty printing is used, you can use ["matplotlib"].
52 52 config=True)
53 53
54 54 use_breqn = CBool(
55 55 True,
56 56 help="Use breqn.sty to automatically break long equations. "
57 57 "This configuration takes effect only for dvipng backend.",
58 58 config=True)
59 59
60 60 packages = List(
61 61 ['amsmath', 'amsthm', 'amssymb', 'bm'],
62 62 help="A list of packages to use for dvipng backend. "
63 63 "'breqn' will be automatically appended when use_breqn=True.",
64 64 config=True)
65 65
66 66 preamble = CUnicode(
67 67 help="Additional preamble to use when generating LaTeX source "
68 68 "for dvipng backend.",
69 69 config=True)
70 70
71 71
72 72 def latex_to_png(s, encode=False, backend=None, wrap=False):
73 73 """Render a LaTeX string to PNG.
74 74
75 75 Parameters
76 76 ----------
77 77 s : str
78 78 The raw string containing valid inline LaTeX.
79 79 encode : bool, optional
80 80 Should the PNG data bebase64 encoded to make it JSON'able.
81 81 backend : {matplotlib, dvipng}
82 82 Backend for producing PNG data.
83 83 wrap : bool
84 84 If true, Automatically wrap `s` as a LaTeX equation.
85 85
86 86 None is returned when the backend cannot be used.
87 87
88 88 """
89 89 allowed_backends = LaTeXTool.instance().backends
90 90 if backend is None:
91 91 backend = allowed_backends[0]
92 92 if backend not in allowed_backends:
93 93 return None
94 94 if backend == 'matplotlib':
95 95 f = latex_to_png_mpl
96 96 elif backend == 'dvipng':
97 97 f = latex_to_png_dvipng
98 98 else:
99 99 raise ValueError('No such backend {0}'.format(backend))
100 100 bin_data = f(s, wrap)
101 101 if encode and bin_data:
102 102 bin_data = encodestring(bin_data)
103 103 return bin_data
104 104
105 105
106 106 def latex_to_png_mpl(s, wrap):
107 107 try:
108 108 from matplotlib import mathtext
109 109 except ImportError:
110 110 return None
111 111
112 112 if wrap:
113 113 s = '${0}$'.format(s)
114 114 mt = mathtext.MathTextParser('bitmap')
115 115 f = BytesIO()
116 116 mt.to_png(f, s, fontsize=12)
117 117 return f.getvalue()
118 118
119 119
120 120 def latex_to_png_dvipng(s, wrap):
121 121 try:
122 122 find_cmd('latex')
123 123 find_cmd('dvipng')
124 124 except FindCmdError:
125 125 return None
126 126 try:
127 127 workdir = tempfile.mkdtemp()
128 128 tmpfile = os.path.join(workdir, "tmp.tex")
129 129 dvifile = os.path.join(workdir, "tmp.dvi")
130 130 outfile = os.path.join(workdir, "tmp.png")
131 131
132 132 with open(tmpfile, "w") as f:
133 133 f.writelines(genelatex(s, wrap))
134 134
135 135 with open(os.devnull, 'w') as devnull:
136 136 subprocess.check_call(
137 ["latex", "-halt-on-error", tmpfile], cwd=workdir,
138 stdout=devnull, stderr=devnull)
137 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
138 cwd=workdir, stdout=devnull, stderr=devnull)
139 139
140 140 subprocess.check_call(
141 141 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
142 142 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
143 143 stdout=devnull, stderr=devnull)
144 144
145 145 with open(outfile, "rb") as f:
146 146 return f.read()
147 147 finally:
148 148 shutil.rmtree(workdir)
149 149
150 150
151 151 def kpsewhich(filename):
152 152 """Invoke kpsewhich command with an argument `filename`."""
153 153 try:
154 154 find_cmd("kpsewhich")
155 155 proc = subprocess.Popen(
156 156 ["kpsewhich", filename],
157 157 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
158 158 (stdout, stderr) = proc.communicate()
159 159 return stdout.strip()
160 160 except FindCmdError:
161 161 pass
162 162
163 163
164 164 def genelatex(body, wrap):
165 165 """Generate LaTeX document for dvipng backend."""
166 166 lt = LaTeXTool.instance()
167 167 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
168 168 yield r'\documentclass{article}'
169 169 packages = lt.packages
170 170 if breqn:
171 171 packages = packages + ['breqn']
172 172 for pack in packages:
173 173 yield r'\usepackage{{{0}}}'.format(pack)
174 174 yield r'\pagestyle{empty}'
175 175 if lt.preamble:
176 176 yield lt.preamble
177 177 yield r'\begin{document}'
178 178 if breqn:
179 179 yield r'\begin{dmath*}'
180 180 yield body
181 181 yield r'\end{dmath*}'
182 182 elif wrap:
183 183 yield '$${0}$$'.format(body)
184 184 else:
185 185 yield body
186 186 yield r'\end{document}'
187 187
188 188
189 189 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
190 190
191 191 def latex_to_html(s, alt='image'):
192 192 """Render LaTeX to HTML with embedded PNG data using data URIs.
193 193
194 194 Parameters
195 195 ----------
196 196 s : str
197 197 The raw string containing valid inline LateX.
198 198 alt : str
199 199 The alt text to use for the HTML.
200 200 """
201 201 base64_data = bytes_to_str(latex_to_png(s, encode=True), 'ascii')
202 202 if base64_data:
203 203 return _data_uri_template_png % (base64_data, alt)
204 204
205 205
206 206 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
207 207 # will remove.
208 208 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
209 209 """
210 210 Given a math expression, renders it in a closely-clipped bounding
211 211 box to an image file.
212 212
213 213 *s*
214 214 A math expression. The math portion should be enclosed in
215 215 dollar signs.
216 216
217 217 *filename_or_obj*
218 218 A filepath or writable file-like object to write the image data
219 219 to.
220 220
221 221 *prop*
222 222 If provided, a FontProperties() object describing the size and
223 223 style of the text.
224 224
225 225 *dpi*
226 226 Override the output dpi, otherwise use the default associated
227 227 with the output format.
228 228
229 229 *format*
230 230 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
231 231 provided, will be deduced from the filename.
232 232 """
233 233 from matplotlib import figure
234 234 # backend_agg supports all of the core output formats
235 235 from matplotlib.backends import backend_agg
236 236 from matplotlib.font_manager import FontProperties
237 237 from matplotlib.mathtext import MathTextParser
238 238
239 239 if prop is None:
240 240 prop = FontProperties()
241 241
242 242 parser = MathTextParser('path')
243 243 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
244 244
245 245 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
246 246 fig.text(0, depth/height, s, fontproperties=prop)
247 247 backend_agg.FigureCanvasAgg(fig)
248 248 fig.savefig(filename_or_obj, dpi=dpi, format=format)
249 249
250 250 return depth
251 251
General Comments 0
You need to be logged in to leave comments. Login now