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