##// END OF EJS Templates
Fix one more deprecated trait type vs. instance case
Jason Grout -
Show More
@@ -1,195 +1,195 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 from base64 import encodestring
8 from base64 import encodestring
9 import os
9 import os
10 import tempfile
10 import tempfile
11 import shutil
11 import shutil
12 import subprocess
12 import subprocess
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
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 config=True)
38 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 config=True)
44 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 config=True)
50 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 config=True)
55 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 : text
63 s : text
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 = encodestring(bin_data)
89 bin_data = encodestring(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 except ImportError:
96 except ImportError:
97 return None
97 return None
98
98
99 # mpl mathtext doesn't support display math, force inline
99 # mpl mathtext doesn't support display math, force inline
100 s = s.replace('$$', '$')
100 s = s.replace('$$', '$')
101 if wrap:
101 if wrap:
102 s = u'${0}$'.format(s)
102 s = u'${0}$'.format(s)
103
103
104 mt = mathtext.MathTextParser('bitmap')
104 mt = mathtext.MathTextParser('bitmap')
105 f = BytesIO()
105 f = BytesIO()
106 mt.to_png(f, s, fontsize=12)
106 mt.to_png(f, s, fontsize=12)
107 return f.getvalue()
107 return f.getvalue()
108
108
109
109
110 def latex_to_png_dvipng(s, wrap):
110 def latex_to_png_dvipng(s, wrap):
111 try:
111 try:
112 find_cmd('latex')
112 find_cmd('latex')
113 find_cmd('dvipng')
113 find_cmd('dvipng')
114 except FindCmdError:
114 except FindCmdError:
115 return None
115 return None
116 try:
116 try:
117 workdir = tempfile.mkdtemp()
117 workdir = tempfile.mkdtemp()
118 tmpfile = os.path.join(workdir, "tmp.tex")
118 tmpfile = os.path.join(workdir, "tmp.tex")
119 dvifile = os.path.join(workdir, "tmp.dvi")
119 dvifile = os.path.join(workdir, "tmp.dvi")
120 outfile = os.path.join(workdir, "tmp.png")
120 outfile = os.path.join(workdir, "tmp.png")
121
121
122 with open(tmpfile, "w", encoding='utf8') as f:
122 with open(tmpfile, "w", encoding='utf8') as f:
123 f.writelines(genelatex(s, wrap))
123 f.writelines(genelatex(s, wrap))
124
124
125 with open(os.devnull, 'wb') as devnull:
125 with open(os.devnull, 'wb') as devnull:
126 subprocess.check_call(
126 subprocess.check_call(
127 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
127 ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile],
128 cwd=workdir, stdout=devnull, stderr=devnull)
128 cwd=workdir, stdout=devnull, stderr=devnull)
129
129
130 subprocess.check_call(
130 subprocess.check_call(
131 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
131 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
132 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
132 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
133 stdout=devnull, stderr=devnull)
133 stdout=devnull, stderr=devnull)
134
134
135 with open(outfile, "rb") as f:
135 with open(outfile, "rb") as f:
136 return f.read()
136 return f.read()
137 finally:
137 finally:
138 shutil.rmtree(workdir)
138 shutil.rmtree(workdir)
139
139
140
140
141 def kpsewhich(filename):
141 def kpsewhich(filename):
142 """Invoke kpsewhich command with an argument `filename`."""
142 """Invoke kpsewhich command with an argument `filename`."""
143 try:
143 try:
144 find_cmd("kpsewhich")
144 find_cmd("kpsewhich")
145 proc = subprocess.Popen(
145 proc = subprocess.Popen(
146 ["kpsewhich", filename],
146 ["kpsewhich", filename],
147 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
147 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
148 (stdout, stderr) = proc.communicate()
148 (stdout, stderr) = proc.communicate()
149 return stdout.strip().decode('utf8', 'replace')
149 return stdout.strip().decode('utf8', 'replace')
150 except FindCmdError:
150 except FindCmdError:
151 pass
151 pass
152
152
153
153
154 def genelatex(body, wrap):
154 def genelatex(body, wrap):
155 """Generate LaTeX document for dvipng backend."""
155 """Generate LaTeX document for dvipng backend."""
156 lt = LaTeXTool.instance()
156 lt = LaTeXTool.instance()
157 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
157 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
158 yield u(r'\documentclass{article}')
158 yield u(r'\documentclass{article}')
159 packages = lt.packages
159 packages = lt.packages
160 if breqn:
160 if breqn:
161 packages = packages + ['breqn']
161 packages = packages + ['breqn']
162 for pack in packages:
162 for pack in packages:
163 yield u(r'\usepackage{{{0}}}'.format(pack))
163 yield u(r'\usepackage{{{0}}}'.format(pack))
164 yield u(r'\pagestyle{empty}')
164 yield u(r'\pagestyle{empty}')
165 if lt.preamble:
165 if lt.preamble:
166 yield lt.preamble
166 yield lt.preamble
167 yield u(r'\begin{document}')
167 yield u(r'\begin{document}')
168 if breqn:
168 if breqn:
169 yield u(r'\begin{dmath*}')
169 yield u(r'\begin{dmath*}')
170 yield body
170 yield body
171 yield u(r'\end{dmath*}')
171 yield u(r'\end{dmath*}')
172 elif wrap:
172 elif wrap:
173 yield u'$${0}$$'.format(body)
173 yield u'$${0}$$'.format(body)
174 else:
174 else:
175 yield body
175 yield body
176 yield u'\end{document}'
176 yield u'\end{document}'
177
177
178
178
179 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
179 _data_uri_template_png = u"""<img src="data:image/png;base64,%s" alt=%s />"""
180
180
181 def latex_to_html(s, alt='image'):
181 def latex_to_html(s, alt='image'):
182 """Render LaTeX to HTML with embedded PNG data using data URIs.
182 """Render LaTeX to HTML with embedded PNG data using data URIs.
183
183
184 Parameters
184 Parameters
185 ----------
185 ----------
186 s : str
186 s : str
187 The raw string containing valid inline LateX.
187 The raw string containing valid inline LateX.
188 alt : str
188 alt : str
189 The alt text to use for the HTML.
189 The alt text to use for the HTML.
190 """
190 """
191 base64_data = latex_to_png(s, encode=True).decode('ascii')
191 base64_data = latex_to_png(s, encode=True).decode('ascii')
192 if base64_data:
192 if base64_data:
193 return _data_uri_template_png % (base64_data, alt)
193 return _data_uri_template_png % (base64_data, alt)
194
194
195
195
General Comments 0
You need to be logged in to leave comments. Login now