##// END OF EJS Templates
Fix for latextools commandline arguments...
Jörgen Stenarson -
Show More
@@ -1,251 +1,252
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 Instance, 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 subprocess.check_call(
136 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
137 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
138
139 subprocess.check_call(
140 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
141 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
142 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
135 with open(os.devnull, 'w') as devnull:
136 subprocess.check_call(
137 ["latex", "-halt-on-error", tmpfile], cwd=workdir,
138 stdout=devnull, stderr=devnull)
139
140 subprocess.check_call(
141 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
142 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
143 stdout=devnull, stderr=devnull)
143 144
144 145 with open(outfile, "rb") as f:
145 146 bin_data = f.read()
146 147 finally:
147 148 shutil.rmtree(workdir)
148 149 return bin_data
149 150
150 151
151 152 def kpsewhich(filename):
152 153 """Invoke kpsewhich command with an argument `filename`."""
153 154 try:
154 155 find_cmd("kpsewhich")
155 156 proc = subprocess.Popen(
156 157 ["kpsewhich", filename],
157 158 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
158 159 (stdout, stderr) = proc.communicate()
159 160 return stdout.strip()
160 161 except FindCmdError:
161 162 pass
162 163
163 164
164 165 def genelatex(body, wrap):
165 166 """Generate LaTeX document for dvipng backend."""
166 167 lt = LaTeXTool.instance()
167 168 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
168 169 yield r'\documentclass{article}'
169 170 packages = lt.packages
170 171 if breqn:
171 172 packages = packages + ['breqn']
172 173 for pack in packages:
173 174 yield r'\usepackage{{{0}}}'.format(pack)
174 175 yield r'\pagestyle{empty}'
175 176 if lt.preamble:
176 177 yield lt.preamble
177 178 yield r'\begin{document}'
178 179 if breqn:
179 180 yield r'\begin{dmath*}'
180 181 yield body
181 182 yield r'\end{dmath*}'
182 183 elif wrap:
183 184 yield '$${0}$$'.format(body)
184 185 else:
185 186 yield body
186 187 yield r'\end{document}'
187 188
188 189
189 190 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
190 191
191 192 def latex_to_html(s, alt='image'):
192 193 """Render LaTeX to HTML with embedded PNG data using data URIs.
193 194
194 195 Parameters
195 196 ----------
196 197 s : str
197 198 The raw string containing valid inline LateX.
198 199 alt : str
199 200 The alt text to use for the HTML.
200 201 """
201 202 base64_data = bytes_to_str(latex_to_png(s, encode=True), 'ascii')
202 203 if base64_data:
203 204 return _data_uri_template_png % (base64_data, alt)
204 205
205 206
206 207 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
207 208 # will remove.
208 209 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
209 210 """
210 211 Given a math expression, renders it in a closely-clipped bounding
211 212 box to an image file.
212 213
213 214 *s*
214 215 A math expression. The math portion should be enclosed in
215 216 dollar signs.
216 217
217 218 *filename_or_obj*
218 219 A filepath or writable file-like object to write the image data
219 220 to.
220 221
221 222 *prop*
222 223 If provided, a FontProperties() object describing the size and
223 224 style of the text.
224 225
225 226 *dpi*
226 227 Override the output dpi, otherwise use the default associated
227 228 with the output format.
228 229
229 230 *format*
230 231 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
231 232 provided, will be deduced from the filename.
232 233 """
233 234 from matplotlib import figure
234 235 # backend_agg supports all of the core output formats
235 236 from matplotlib.backends import backend_agg
236 237 from matplotlib.font_manager import FontProperties
237 238 from matplotlib.mathtext import MathTextParser
238 239
239 240 if prop is None:
240 241 prop = FontProperties()
241 242
242 243 parser = MathTextParser('path')
243 244 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
244 245
245 246 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
246 247 fig.text(0, depth/height, s, fontproperties=prop)
247 248 backend_agg.FigureCanvasAgg(fig)
248 249 fig.savefig(filename_or_obj, dpi=dpi, format=format)
249 250
250 251 return depth
251 252
General Comments 0
You need to be logged in to leave comments. Login now