##// END OF EJS Templates
remove unused import
Srinivas Reddy Thatiparthy -
Show More
@@ -1,201 +1,201 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for handling LaTeX."""
2 """Tools for handling LaTeX."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from io import BytesIO, open
7 from io import BytesIO, open
8 import os
8 import os
9 import tempfile
9 import tempfile
10 import shutil
10 import shutil
11 import subprocess
11 import subprocess
12 from base64 import encodebytes
12 from base64 import encodebytes
13
13
14 from IPython.utils.process import find_cmd, FindCmdError
14 from IPython.utils.process import find_cmd, FindCmdError
15 from traitlets.config import get_config
15 from traitlets.config import get_config
16 from traitlets.config.configurable import SingletonConfigurable
16 from traitlets.config.configurable import SingletonConfigurable
17 from traitlets import List, Bool, Unicode
17 from traitlets import List, Bool, Unicode
18 from IPython.utils.py3compat import cast_unicode, cast_unicode_py2 as u, PY3
18 from IPython.utils.py3compat import cast_unicode, cast_unicode_py2 as u
19
19
20
20
21 class LaTeXTool(SingletonConfigurable):
21 class LaTeXTool(SingletonConfigurable):
22 """An object to store configuration of the LaTeX tool."""
22 """An object to store configuration of the LaTeX tool."""
23 def _config_default(self):
23 def _config_default(self):
24 return get_config()
24 return get_config()
25
25
26 backends = List(
26 backends = List(
27 Unicode(), ["matplotlib", "dvipng"],
27 Unicode(), ["matplotlib", "dvipng"],
28 help="Preferred backend to draw LaTeX math equations. "
28 help="Preferred backend to draw LaTeX math equations. "
29 "Backends in the list are checked one by one and the first "
29 "Backends in the list are checked one by one and the first "
30 "usable one is used. Note that `matplotlib` backend "
30 "usable one is used. Note that `matplotlib` backend "
31 "is usable only for inline style equations. To draw "
31 "is usable only for inline style equations. To draw "
32 "display style equations, `dvipng` backend must be specified. ",
32 "display style equations, `dvipng` backend must be specified. ",
33 # It is a List instead of Enum, to make configuration more
33 # It is a List instead of Enum, to make configuration more
34 # flexible. For example, to use matplotlib mainly but dvipng
34 # flexible. For example, to use matplotlib mainly but dvipng
35 # for display style, the default ["matplotlib", "dvipng"] can
35 # for display style, the default ["matplotlib", "dvipng"] can
36 # be used. To NOT use dvipng so that other repr such as
36 # be used. To NOT use dvipng so that other repr such as
37 # unicode pretty printing is used, you can use ["matplotlib"].
37 # unicode pretty printing is used, you can use ["matplotlib"].
38 ).tag(config=True)
38 ).tag(config=True)
39
39
40 use_breqn = Bool(
40 use_breqn = Bool(
41 True,
41 True,
42 help="Use breqn.sty to automatically break long equations. "
42 help="Use breqn.sty to automatically break long equations. "
43 "This configuration takes effect only for dvipng backend.",
43 "This configuration takes effect only for dvipng backend.",
44 ).tag(config=True)
44 ).tag(config=True)
45
45
46 packages = List(
46 packages = List(
47 ['amsmath', 'amsthm', 'amssymb', 'bm'],
47 ['amsmath', 'amsthm', 'amssymb', 'bm'],
48 help="A list of packages to use for dvipng backend. "
48 help="A list of packages to use for dvipng backend. "
49 "'breqn' will be automatically appended when use_breqn=True.",
49 "'breqn' will be automatically appended when use_breqn=True.",
50 ).tag(config=True)
50 ).tag(config=True)
51
51
52 preamble = Unicode(
52 preamble = Unicode(
53 help="Additional preamble to use when generating LaTeX source "
53 help="Additional preamble to use when generating LaTeX source "
54 "for dvipng backend.",
54 "for dvipng backend.",
55 ).tag(config=True)
55 ).tag(config=True)
56
56
57
57
58 def latex_to_png(s, encode=False, backend=None, wrap=False):
58 def latex_to_png(s, encode=False, backend=None, wrap=False):
59 """Render a LaTeX string to PNG.
59 """Render a LaTeX string to PNG.
60
60
61 Parameters
61 Parameters
62 ----------
62 ----------
63 s : str
63 s : str
64 The raw string containing valid inline LaTeX.
64 The raw string containing valid inline LaTeX.
65 encode : bool, optional
65 encode : bool, optional
66 Should the PNG data base64 encoded to make it JSON'able.
66 Should the PNG data base64 encoded to make it JSON'able.
67 backend : {matplotlib, dvipng}
67 backend : {matplotlib, dvipng}
68 Backend for producing PNG data.
68 Backend for producing PNG data.
69 wrap : bool
69 wrap : bool
70 If true, Automatically wrap `s` as a LaTeX equation.
70 If true, Automatically wrap `s` as a LaTeX equation.
71
71
72 None is returned when the backend cannot be used.
72 None is returned when the backend cannot be used.
73
73
74 """
74 """
75 s = cast_unicode(s)
75 s = cast_unicode(s)
76 allowed_backends = LaTeXTool.instance().backends
76 allowed_backends = LaTeXTool.instance().backends
77 if backend is None:
77 if backend is None:
78 backend = allowed_backends[0]
78 backend = allowed_backends[0]
79 if backend not in allowed_backends:
79 if backend not in allowed_backends:
80 return None
80 return None
81 if backend == 'matplotlib':
81 if backend == 'matplotlib':
82 f = latex_to_png_mpl
82 f = latex_to_png_mpl
83 elif backend == 'dvipng':
83 elif backend == 'dvipng':
84 f = latex_to_png_dvipng
84 f = latex_to_png_dvipng
85 else:
85 else:
86 raise ValueError('No such backend {0}'.format(backend))
86 raise ValueError('No such backend {0}'.format(backend))
87 bin_data = f(s, wrap)
87 bin_data = f(s, wrap)
88 if encode and bin_data:
88 if encode and bin_data:
89 bin_data = encodebytes(bin_data)
89 bin_data = encodebytes(bin_data)
90 return bin_data
90 return bin_data
91
91
92
92
93 def latex_to_png_mpl(s, wrap):
93 def latex_to_png_mpl(s, wrap):
94 try:
94 try:
95 from matplotlib import mathtext
95 from matplotlib import mathtext
96 from pyparsing import ParseFatalException
96 from pyparsing import ParseFatalException
97 except ImportError:
97 except ImportError:
98 return None
98 return None
99
99
100 # mpl mathtext doesn't support display math, force inline
100 # mpl mathtext doesn't support display math, force inline
101 s = s.replace('$$', '$')
101 s = s.replace('$$', '$')
102 if wrap:
102 if wrap:
103 s = u'${0}$'.format(s)
103 s = u'${0}$'.format(s)
104
104
105 try:
105 try:
106 mt = mathtext.MathTextParser('bitmap')
106 mt = mathtext.MathTextParser('bitmap')
107 f = BytesIO()
107 f = BytesIO()
108 mt.to_png(f, s, fontsize=12)
108 mt.to_png(f, s, fontsize=12)
109 return f.getvalue()
109 return f.getvalue()
110 except (ValueError, RuntimeError, ParseFatalException):
110 except (ValueError, RuntimeError, ParseFatalException):
111 return None
111 return None
112
112
113
113
114 def latex_to_png_dvipng(s, wrap):
114 def latex_to_png_dvipng(s, wrap):
115 try:
115 try:
116 find_cmd('latex')
116 find_cmd('latex')
117 find_cmd('dvipng')
117 find_cmd('dvipng')
118 except FindCmdError:
118 except FindCmdError:
119 return None
119 return None
120 try:
120 try:
121 workdir = tempfile.mkdtemp()
121 workdir = tempfile.mkdtemp()
122 tmpfile = os.path.join(workdir, "tmp.tex")
122 tmpfile = os.path.join(workdir, "tmp.tex")
123 dvifile = os.path.join(workdir, "tmp.dvi")
123 dvifile = os.path.join(workdir, "tmp.dvi")
124 outfile = os.path.join(workdir, "tmp.png")
124 outfile = os.path.join(workdir, "tmp.png")
125
125
126 with open(tmpfile, "w", encoding='utf8') as f:
126 with open(tmpfile, "w", encoding='utf8') as f:
127 f.writelines(genelatex(s, wrap))
127 f.writelines(genelatex(s, wrap))
128
128
129 with open(os.devnull, 'wb') as devnull:
129 with open(os.devnull, 'wb') as devnull:
130 subprocess.check_call(
130 subprocess.check_call(
131 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
131 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
132 cwd=workdir, stdout=devnull, stderr=devnull)
132 cwd=workdir, stdout=devnull, stderr=devnull)
133
133
134 subprocess.check_call(
134 subprocess.check_call(
135 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
135 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
136 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
136 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
137 stdout=devnull, stderr=devnull)
137 stdout=devnull, stderr=devnull)
138
138
139 with open(outfile, "rb") as f:
139 with open(outfile, "rb") as f:
140 return f.read()
140 return f.read()
141 except subprocess.CalledProcessError:
141 except subprocess.CalledProcessError:
142 return None
142 return None
143 finally:
143 finally:
144 shutil.rmtree(workdir)
144 shutil.rmtree(workdir)
145
145
146
146
147 def kpsewhich(filename):
147 def kpsewhich(filename):
148 """Invoke kpsewhich command with an argument `filename`."""
148 """Invoke kpsewhich command with an argument `filename`."""
149 try:
149 try:
150 find_cmd("kpsewhich")
150 find_cmd("kpsewhich")
151 proc = subprocess.Popen(
151 proc = subprocess.Popen(
152 ["kpsewhich", filename],
152 ["kpsewhich", filename],
153 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
153 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
154 (stdout, stderr) = proc.communicate()
154 (stdout, stderr) = proc.communicate()
155 return stdout.strip().decode('utf8', 'replace')
155 return stdout.strip().decode('utf8', 'replace')
156 except FindCmdError:
156 except FindCmdError:
157 pass
157 pass
158
158
159
159
160 def genelatex(body, wrap):
160 def genelatex(body, wrap):
161 """Generate LaTeX document for dvipng backend."""
161 """Generate LaTeX document for dvipng backend."""
162 lt = LaTeXTool.instance()
162 lt = LaTeXTool.instance()
163 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
163 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
164 yield u(r'\documentclass{article}')
164 yield u(r'\documentclass{article}')
165 packages = lt.packages
165 packages = lt.packages
166 if breqn:
166 if breqn:
167 packages = packages + ['breqn']
167 packages = packages + ['breqn']
168 for pack in packages:
168 for pack in packages:
169 yield u(r'\usepackage{{{0}}}'.format(pack))
169 yield u(r'\usepackage{{{0}}}'.format(pack))
170 yield u(r'\pagestyle{empty}')
170 yield u(r'\pagestyle{empty}')
171 if lt.preamble:
171 if lt.preamble:
172 yield lt.preamble
172 yield lt.preamble
173 yield u(r'\begin{document}')
173 yield u(r'\begin{document}')
174 if breqn:
174 if breqn:
175 yield u(r'\begin{dmath*}')
175 yield u(r'\begin{dmath*}')
176 yield body
176 yield body
177 yield u(r'\end{dmath*}')
177 yield u(r'\end{dmath*}')
178 elif wrap:
178 elif wrap:
179 yield u'$${0}$$'.format(body)
179 yield u'$${0}$$'.format(body)
180 else:
180 else:
181 yield body
181 yield body
182 yield u'\end{document}'
182 yield u'\end{document}'
183
183
184
184
185 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
185 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
186
186
187 def latex_to_html(s, alt='image'):
187 def latex_to_html(s, alt='image'):
188 """Render LaTeX to HTML with embedded PNG data using data URIs.
188 """Render LaTeX to HTML with embedded PNG data using data URIs.
189
189
190 Parameters
190 Parameters
191 ----------
191 ----------
192 s : str
192 s : str
193 The raw string containing valid inline LateX.
193 The raw string containing valid inline LateX.
194 alt : str
194 alt : str
195 The alt text to use for the HTML.
195 The alt text to use for the HTML.
196 """
196 """
197 base64_data = latex_to_png(s, encode=True).decode('ascii')
197 base64_data = latex_to_png(s, encode=True).decode('ascii')
198 if base64_data:
198 if base64_data:
199 return _data_uri_template_png % (base64_data, alt)
199 return _data_uri_template_png % (base64_data, alt)
200
200
201
201
General Comments 0
You need to be logged in to leave comments. Login now