##// END OF EJS Templates
Check if latex/dvipng exist before calling them
Takafumi Arakaki -
Show More
@@ -1,176 +1,181 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for handling LaTeX.
2 """Tools for handling LaTeX.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2010 IPython Development Team.
9 # Copyright (C) 2010 IPython Development Team.
10 #
10 #
11 # Distributed under the terms of the Modified BSD License.
11 # Distributed under the terms of the Modified BSD License.
12 #
12 #
13 # The full license is in the file COPYING.txt, distributed with this software.
13 # The full license is in the file COPYING.txt, distributed with this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from StringIO import StringIO
20 from StringIO import StringIO
21 from base64 import encodestring
21 from base64 import encodestring
22 import os
22 import os
23 import tempfile
23 import tempfile
24 import shutil
24 import shutil
25 import subprocess
25 import subprocess
26
26
27 from IPython.utils.process import find_cmd, FindCmdError
28
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28 # Tools
30 # Tools
29 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
30
32
31
33
32 def latex_to_png(s, encode=False, backend='mpl'):
34 def latex_to_png(s, encode=False, backend='mpl'):
33 """Render a LaTeX string to PNG.
35 """Render a LaTeX string to PNG.
34
36
35 Parameters
37 Parameters
36 ----------
38 ----------
37 s : str
39 s : str
38 The raw string containing valid inline LaTeX.
40 The raw string containing valid inline LaTeX.
39 encode : bool, optional
41 encode : bool, optional
40 Should the PNG data bebase64 encoded to make it JSON'able.
42 Should the PNG data bebase64 encoded to make it JSON'able.
41 backend : {mpl, dvipng}
43 backend : {mpl, dvipng}
42 Backend for producing PNG data.
44 Backend for producing PNG data.
43
45
44 None is returned when the backend cannot be used.
46 None is returned when the backend cannot be used.
45
47
46 """
48 """
47 if backend == 'mpl':
49 if backend == 'mpl':
48 f = latex_to_png_mpl
50 f = latex_to_png_mpl
49 elif backend == 'dvipng':
51 elif backend == 'dvipng':
50 f = latex_to_png_dvipng
52 f = latex_to_png_dvipng
51 else:
53 else:
52 raise ValueError('No such backend {0}'.format(backend))
54 raise ValueError('No such backend {0}'.format(backend))
53 bin_data = f(s)
55 bin_data = f(s)
54 if encode and bin_data:
56 if encode and bin_data:
55 bin_data = encodestring(bin_data)
57 bin_data = encodestring(bin_data)
56 return bin_data
58 return bin_data
57
59
58
60
59 def latex_to_png_mpl(s):
61 def latex_to_png_mpl(s):
60 try:
62 try:
61 from matplotlib import mathtext
63 from matplotlib import mathtext
62 except ImportError:
64 except ImportError:
63 return None
65 return None
64
66
65 mt = mathtext.MathTextParser('bitmap')
67 mt = mathtext.MathTextParser('bitmap')
66 f = StringIO()
68 f = StringIO()
67 mt.to_png(f, s, fontsize=12)
69 mt.to_png(f, s, fontsize=12)
68 return f.getvalue()
70 return f.getvalue()
69
71
70
72
71 def latex_to_png_dvipng(s):
73 def latex_to_png_dvipng(s):
72 try:
74 try:
75 find_cmd('latex')
76 find_cmd('dvipng')
77 except FindCmdError:
78 return None
79 try:
73 workdir = tempfile.mkdtemp()
80 workdir = tempfile.mkdtemp()
74 tmpfile = os.path.join(workdir, "tmp.tex")
81 tmpfile = os.path.join(workdir, "tmp.tex")
75 dvifile = os.path.join(workdir, "tmp.dvi")
82 dvifile = os.path.join(workdir, "tmp.dvi")
76 outfile = os.path.join(workdir, "tmp.png")
83 outfile = os.path.join(workdir, "tmp.png")
77
84
78 with open(tmpfile, "w") as f:
85 with open(tmpfile, "w") as f:
79 f.write(_latex_header)
86 f.write(_latex_header)
80 f.write(s)
87 f.write(s)
81 f.write(_latex_footer)
88 f.write(_latex_footer)
82
89
83 subprocess.check_call(
90 subprocess.check_call(
84 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
91 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
85 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
92 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
86
93
87 subprocess.check_call(
94 subprocess.check_call(
88 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
95 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
89 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
96 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
90 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
91
98
92 with open(outfile) as f:
99 with open(outfile) as f:
93 bin_data = f.read()
100 bin_data = f.read()
94 except subprocess.CalledProcessError:
95 bin_data = None
96 finally:
101 finally:
97 shutil.rmtree(workdir)
102 shutil.rmtree(workdir)
98 return bin_data
103 return bin_data
99
104
100
105
101 _latex_header = r'''
106 _latex_header = r'''
102 \documentclass{article}
107 \documentclass{article}
103 \usepackage{amsmath}
108 \usepackage{amsmath}
104 \usepackage{amsthm}
109 \usepackage{amsthm}
105 \usepackage{amssymb}
110 \usepackage{amssymb}
106 \usepackage{bm}
111 \usepackage{bm}
107 \pagestyle{empty}
112 \pagestyle{empty}
108 \begin{document}
113 \begin{document}
109 '''
114 '''
110
115
111 _latex_footer = r'\end{document}'
116 _latex_footer = r'\end{document}'
112
117
113
118
114 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
119 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
115
120
116 def latex_to_html(s, alt='image'):
121 def latex_to_html(s, alt='image'):
117 """Render LaTeX to HTML with embedded PNG data using data URIs.
122 """Render LaTeX to HTML with embedded PNG data using data URIs.
118
123
119 Parameters
124 Parameters
120 ----------
125 ----------
121 s : str
126 s : str
122 The raw string containing valid inline LateX.
127 The raw string containing valid inline LateX.
123 alt : str
128 alt : str
124 The alt text to use for the HTML.
129 The alt text to use for the HTML.
125 """
130 """
126 base64_data = latex_to_png(s, encode=True)
131 base64_data = latex_to_png(s, encode=True)
127 if base64_data:
132 if base64_data:
128 return _data_uri_template_png % (base64_data, alt)
133 return _data_uri_template_png % (base64_data, alt)
129
134
130
135
131 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
136 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
132 # will remove.
137 # will remove.
133 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
138 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
134 """
139 """
135 Given a math expression, renders it in a closely-clipped bounding
140 Given a math expression, renders it in a closely-clipped bounding
136 box to an image file.
141 box to an image file.
137
142
138 *s*
143 *s*
139 A math expression. The math portion should be enclosed in
144 A math expression. The math portion should be enclosed in
140 dollar signs.
145 dollar signs.
141
146
142 *filename_or_obj*
147 *filename_or_obj*
143 A filepath or writable file-like object to write the image data
148 A filepath or writable file-like object to write the image data
144 to.
149 to.
145
150
146 *prop*
151 *prop*
147 If provided, a FontProperties() object describing the size and
152 If provided, a FontProperties() object describing the size and
148 style of the text.
153 style of the text.
149
154
150 *dpi*
155 *dpi*
151 Override the output dpi, otherwise use the default associated
156 Override the output dpi, otherwise use the default associated
152 with the output format.
157 with the output format.
153
158
154 *format*
159 *format*
155 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
160 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
156 provided, will be deduced from the filename.
161 provided, will be deduced from the filename.
157 """
162 """
158 from matplotlib import figure
163 from matplotlib import figure
159 # backend_agg supports all of the core output formats
164 # backend_agg supports all of the core output formats
160 from matplotlib.backends import backend_agg
165 from matplotlib.backends import backend_agg
161 from matplotlib.font_manager import FontProperties
166 from matplotlib.font_manager import FontProperties
162 from matplotlib.mathtext import MathTextParser
167 from matplotlib.mathtext import MathTextParser
163
168
164 if prop is None:
169 if prop is None:
165 prop = FontProperties()
170 prop = FontProperties()
166
171
167 parser = MathTextParser('path')
172 parser = MathTextParser('path')
168 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
173 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
169
174
170 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
175 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
171 fig.text(0, depth/height, s, fontproperties=prop)
176 fig.text(0, depth/height, s, fontproperties=prop)
172 backend_agg.FigureCanvasAgg(fig)
177 backend_agg.FigureCanvasAgg(fig)
173 fig.savefig(filename_or_obj, dpi=dpi, format=format)
178 fig.savefig(filename_or_obj, dpi=dpi, format=format)
174
179
175 return depth
180 return depth
176
181
General Comments 0
You need to be logged in to leave comments. Login now