##// END OF EJS Templates
Fix IPython.lib.latextools for Python 3
Thomas Kluyver -
Show More
@@ -1,250 +1,251 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 io import BytesIO
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
27 from IPython.utils.process import find_cmd, FindCmdError
28 from IPython.config.configurable import SingletonConfigurable
28 from IPython.config.configurable import SingletonConfigurable
29 from IPython.utils.traitlets import Instance, List, CBool, CUnicode
29 from IPython.utils.traitlets import Instance, List, CBool, CUnicode
30 from IPython.utils.py3compat import bytes_to_str
30
31
31 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
32 # Tools
33 # Tools
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34
35
35
36
36 class LaTeXTool(SingletonConfigurable):
37 class LaTeXTool(SingletonConfigurable):
37 """An object to store configuration of the LaTeX tool."""
38 """An object to store configuration of the LaTeX tool."""
38
39
39 backends = List(
40 backends = List(
40 CUnicode, ["matplotlib", "dvipng"],
41 CUnicode, ["matplotlib", "dvipng"],
41 help="Preferred backend to draw LaTeX math equations. "
42 help="Preferred backend to draw LaTeX math equations. "
42 "Backends in the list are checked one by one and the first "
43 "Backends in the list are checked one by one and the first "
43 "usable one is used. Note that `matplotlib` backend "
44 "usable one is used. Note that `matplotlib` backend "
44 "is usable only for inline style equations. To draw "
45 "is usable only for inline style equations. To draw "
45 "display style equations, `dvipng` backend must be specified. ",
46 "display style equations, `dvipng` backend must be specified. ",
46 # It is a List instead of Enum, to make configuration more
47 # It is a List instead of Enum, to make configuration more
47 # flexible. For example, to use matplotlib mainly but dvipng
48 # flexible. For example, to use matplotlib mainly but dvipng
48 # for display style, the default ["matplotlib", "dvipng"] can
49 # for display style, the default ["matplotlib", "dvipng"] can
49 # be used. To NOT use dvipng so that other repr such as
50 # be used. To NOT use dvipng so that other repr such as
50 # unicode pretty printing is used, you can use ["matplotlib"].
51 # unicode pretty printing is used, you can use ["matplotlib"].
51 config=True)
52 config=True)
52
53
53 use_breqn = CBool(
54 use_breqn = CBool(
54 True,
55 True,
55 help="Use breqn.sty to automatically break long equations. "
56 help="Use breqn.sty to automatically break long equations. "
56 "This configuration takes effect only for dvipng backend.",
57 "This configuration takes effect only for dvipng backend.",
57 config=True)
58 config=True)
58
59
59 packages = List(
60 packages = List(
60 ['amsmath', 'amsthm', 'amssymb', 'bm'],
61 ['amsmath', 'amsthm', 'amssymb', 'bm'],
61 help="A list of packages to use for dvipng backend. "
62 help="A list of packages to use for dvipng backend. "
62 "'breqn' will be automatically appended when use_breqn=True.",
63 "'breqn' will be automatically appended when use_breqn=True.",
63 config=True)
64 config=True)
64
65
65 preamble = CUnicode(
66 preamble = CUnicode(
66 help="Additional preamble to use when generating LaTeX source "
67 help="Additional preamble to use when generating LaTeX source "
67 "for dvipng backend.",
68 "for dvipng backend.",
68 config=True)
69 config=True)
69
70
70
71
71 def latex_to_png(s, encode=False, backend=None, wrap=False):
72 def latex_to_png(s, encode=False, backend=None, wrap=False):
72 """Render a LaTeX string to PNG.
73 """Render a LaTeX string to PNG.
73
74
74 Parameters
75 Parameters
75 ----------
76 ----------
76 s : str
77 s : str
77 The raw string containing valid inline LaTeX.
78 The raw string containing valid inline LaTeX.
78 encode : bool, optional
79 encode : bool, optional
79 Should the PNG data bebase64 encoded to make it JSON'able.
80 Should the PNG data bebase64 encoded to make it JSON'able.
80 backend : {matplotlib, dvipng}
81 backend : {matplotlib, dvipng}
81 Backend for producing PNG data.
82 Backend for producing PNG data.
82 wrap : bool
83 wrap : bool
83 If true, Automatically wrap `s` as a LaTeX equation.
84 If true, Automatically wrap `s` as a LaTeX equation.
84
85
85 None is returned when the backend cannot be used.
86 None is returned when the backend cannot be used.
86
87
87 """
88 """
88 allowed_backends = LaTeXTool.instance().backends
89 allowed_backends = LaTeXTool.instance().backends
89 if backend is None:
90 if backend is None:
90 backend = allowed_backends[0]
91 backend = allowed_backends[0]
91 if backend not in allowed_backends:
92 if backend not in allowed_backends:
92 return None
93 return None
93 if backend == 'matplotlib':
94 if backend == 'matplotlib':
94 f = latex_to_png_mpl
95 f = latex_to_png_mpl
95 elif backend == 'dvipng':
96 elif backend == 'dvipng':
96 f = latex_to_png_dvipng
97 f = latex_to_png_dvipng
97 else:
98 else:
98 raise ValueError('No such backend {0}'.format(backend))
99 raise ValueError('No such backend {0}'.format(backend))
99 bin_data = f(s, wrap)
100 bin_data = f(s, wrap)
100 if encode and bin_data:
101 if encode and bin_data:
101 bin_data = encodestring(bin_data)
102 bin_data = encodestring(bin_data)
102 return bin_data
103 return bin_data
103
104
104
105
105 def latex_to_png_mpl(s, wrap):
106 def latex_to_png_mpl(s, wrap):
106 try:
107 try:
107 from matplotlib import mathtext
108 from matplotlib import mathtext
108 except ImportError:
109 except ImportError:
109 return None
110 return None
110
111
111 if wrap:
112 if wrap:
112 s = '${0}$'.format(s)
113 s = '${0}$'.format(s)
113 mt = mathtext.MathTextParser('bitmap')
114 mt = mathtext.MathTextParser('bitmap')
114 f = StringIO()
115 f = BytesIO()
115 mt.to_png(f, s, fontsize=12)
116 mt.to_png(f, s, fontsize=12)
116 return f.getvalue()
117 return f.getvalue()
117
118
118
119
119 def latex_to_png_dvipng(s, wrap):
120 def latex_to_png_dvipng(s, wrap):
120 try:
121 try:
121 find_cmd('latex')
122 find_cmd('latex')
122 find_cmd('dvipng')
123 find_cmd('dvipng')
123 except FindCmdError:
124 except FindCmdError:
124 return None
125 return None
125 try:
126 try:
126 workdir = tempfile.mkdtemp()
127 workdir = tempfile.mkdtemp()
127 tmpfile = os.path.join(workdir, "tmp.tex")
128 tmpfile = os.path.join(workdir, "tmp.tex")
128 dvifile = os.path.join(workdir, "tmp.dvi")
129 dvifile = os.path.join(workdir, "tmp.dvi")
129 outfile = os.path.join(workdir, "tmp.png")
130 outfile = os.path.join(workdir, "tmp.png")
130
131
131 with open(tmpfile, "w") as f:
132 with open(tmpfile, "w") as f:
132 f.writelines(genelatex(s, wrap))
133 f.writelines(genelatex(s, wrap))
133
134
134 subprocess.check_call(
135 subprocess.check_call(
135 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
136 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
136 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
137 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
137
138
138 subprocess.check_call(
139 subprocess.check_call(
139 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
140 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
140 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
141 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
141 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
142 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
142
143
143 with open(outfile) as f:
144 with open(outfile, "rb") as f:
144 bin_data = f.read()
145 bin_data = f.read()
145 finally:
146 finally:
146 shutil.rmtree(workdir)
147 shutil.rmtree(workdir)
147 return bin_data
148 return bin_data
148
149
149
150
150 def kpsewhich(filename):
151 def kpsewhich(filename):
151 """Invoke kpsewhich command with an argument `filename`."""
152 """Invoke kpsewhich command with an argument `filename`."""
152 try:
153 try:
153 find_cmd("kpsewhich")
154 find_cmd("kpsewhich")
154 proc = subprocess.Popen(
155 proc = subprocess.Popen(
155 ["kpsewhich", filename],
156 ["kpsewhich", filename],
156 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
157 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
157 (stdout, stderr) = proc.communicate()
158 (stdout, stderr) = proc.communicate()
158 return stdout.strip()
159 return stdout.strip()
159 except FindCmdError:
160 except FindCmdError:
160 pass
161 pass
161
162
162
163
163 def genelatex(body, wrap):
164 def genelatex(body, wrap):
164 """Generate LaTeX document for dvipng backend."""
165 """Generate LaTeX document for dvipng backend."""
165 lt = LaTeXTool.instance()
166 lt = LaTeXTool.instance()
166 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
167 breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty")
167 yield r'\documentclass{article}'
168 yield r'\documentclass{article}'
168 packages = lt.packages
169 packages = lt.packages
169 if breqn:
170 if breqn:
170 packages = packages + ['breqn']
171 packages = packages + ['breqn']
171 for pack in packages:
172 for pack in packages:
172 yield r'\usepackage{{{0}}}'.format(pack)
173 yield r'\usepackage{{{0}}}'.format(pack)
173 yield r'\pagestyle{empty}'
174 yield r'\pagestyle{empty}'
174 if lt.preamble:
175 if lt.preamble:
175 yield lt.preamble
176 yield lt.preamble
176 yield r'\begin{document}'
177 yield r'\begin{document}'
177 if breqn:
178 if breqn:
178 yield r'\begin{dmath*}'
179 yield r'\begin{dmath*}'
179 yield body
180 yield body
180 yield r'\end{dmath*}'
181 yield r'\end{dmath*}'
181 elif wrap:
182 elif wrap:
182 yield '$${0}$$'.format(body)
183 yield '$${0}$$'.format(body)
183 else:
184 else:
184 yield body
185 yield body
185 yield r'\end{document}'
186 yield r'\end{document}'
186
187
187
188
188 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
189 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
189
190
190 def latex_to_html(s, alt='image'):
191 def latex_to_html(s, alt='image'):
191 """Render LaTeX to HTML with embedded PNG data using data URIs.
192 """Render LaTeX to HTML with embedded PNG data using data URIs.
192
193
193 Parameters
194 Parameters
194 ----------
195 ----------
195 s : str
196 s : str
196 The raw string containing valid inline LateX.
197 The raw string containing valid inline LateX.
197 alt : str
198 alt : str
198 The alt text to use for the HTML.
199 The alt text to use for the HTML.
199 """
200 """
200 base64_data = latex_to_png(s, encode=True)
201 base64_data = bytes_to_str(latex_to_png(s, encode=True), 'ascii')
201 if base64_data:
202 if base64_data:
202 return _data_uri_template_png % (base64_data, alt)
203 return _data_uri_template_png % (base64_data, alt)
203
204
204
205
205 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
206 # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we
206 # will remove.
207 # will remove.
207 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
208 def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None):
208 """
209 """
209 Given a math expression, renders it in a closely-clipped bounding
210 Given a math expression, renders it in a closely-clipped bounding
210 box to an image file.
211 box to an image file.
211
212
212 *s*
213 *s*
213 A math expression. The math portion should be enclosed in
214 A math expression. The math portion should be enclosed in
214 dollar signs.
215 dollar signs.
215
216
216 *filename_or_obj*
217 *filename_or_obj*
217 A filepath or writable file-like object to write the image data
218 A filepath or writable file-like object to write the image data
218 to.
219 to.
219
220
220 *prop*
221 *prop*
221 If provided, a FontProperties() object describing the size and
222 If provided, a FontProperties() object describing the size and
222 style of the text.
223 style of the text.
223
224
224 *dpi*
225 *dpi*
225 Override the output dpi, otherwise use the default associated
226 Override the output dpi, otherwise use the default associated
226 with the output format.
227 with the output format.
227
228
228 *format*
229 *format*
229 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
230 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
230 provided, will be deduced from the filename.
231 provided, will be deduced from the filename.
231 """
232 """
232 from matplotlib import figure
233 from matplotlib import figure
233 # backend_agg supports all of the core output formats
234 # backend_agg supports all of the core output formats
234 from matplotlib.backends import backend_agg
235 from matplotlib.backends import backend_agg
235 from matplotlib.font_manager import FontProperties
236 from matplotlib.font_manager import FontProperties
236 from matplotlib.mathtext import MathTextParser
237 from matplotlib.mathtext import MathTextParser
237
238
238 if prop is None:
239 if prop is None:
239 prop = FontProperties()
240 prop = FontProperties()
240
241
241 parser = MathTextParser('path')
242 parser = MathTextParser('path')
242 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
243 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
243
244
244 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
245 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
245 fig.text(0, depth/height, s, fontproperties=prop)
246 fig.text(0, depth/height, s, fontproperties=prop)
246 backend_agg.FigureCanvasAgg(fig)
247 backend_agg.FigureCanvasAgg(fig)
247 fig.savefig(filename_or_obj, dpi=dpi, format=format)
248 fig.savefig(filename_or_obj, dpi=dpi, format=format)
248
249
249 return depth
250 return depth
250
251
@@ -1,119 +1,139 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.utils.path.py"""
2 """Tests for IPython.utils.path.py"""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2011 The IPython Development Team
5 # Copyright (C) 2008-2011 The IPython Development Team
6 #
6 #
7 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 import nose.tools as nt
11 import nose.tools as nt
12
12
13 from IPython.lib import latextools
13 from IPython.lib import latextools
14 from IPython.testing.decorators import onlyif_cmds_exist
14 from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib
15 from IPython.testing.tools import monkeypatch
15 from IPython.testing.tools import monkeypatch
16 from IPython.utils.process import FindCmdError
16 from IPython.utils.process import FindCmdError
17
17
18
18
19 def test_latex_to_png_dvipng_fails_when_no_cmd():
19 def test_latex_to_png_dvipng_fails_when_no_cmd():
20 """
20 """
21 `latex_to_png_dvipng` should return None when there is no required command
21 `latex_to_png_dvipng` should return None when there is no required command
22 """
22 """
23 for command in ['latex', 'dvipng']:
23 for command in ['latex', 'dvipng']:
24 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
24 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
25
25
26
26
27 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
27 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
28 def mock_find_cmd(arg):
28 def mock_find_cmd(arg):
29 if arg == command:
29 if arg == command:
30 raise FindCmdError
30 raise FindCmdError
31
31
32 with monkeypatch(latextools, "find_cmd", mock_find_cmd):
32 with monkeypatch(latextools, "find_cmd", mock_find_cmd):
33 nt.assert_equals(latextools.latex_to_png_dvipng("whatever", True),
33 nt.assert_equals(latextools.latex_to_png_dvipng("whatever", True),
34 None)
34 None)
35
35
36
36
37 @onlyif_cmds_exist('latex', 'dvipng')
37 @onlyif_cmds_exist('latex', 'dvipng')
38 def test_latex_to_png_dvipng_runs():
38 def test_latex_to_png_dvipng_runs():
39 """
39 """
40 Test that latex_to_png_dvipng just runs without error.
40 Test that latex_to_png_dvipng just runs without error.
41 """
41 """
42 def mock_kpsewhich(filename):
42 def mock_kpsewhich(filename):
43 nt.assert_equals(filename, "breqn.sty")
43 nt.assert_equals(filename, "breqn.sty")
44 return None
44 return None
45
45
46 for (s, wrap) in [("$$x^2$$", False), ("x^2", True)]:
46 for (s, wrap) in [("$$x^2$$", False), ("x^2", True)]:
47 yield (latextools.latex_to_png_dvipng, s, wrap)
47 yield (latextools.latex_to_png_dvipng, s, wrap)
48
48
49 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
49 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
50 yield (latextools.latex_to_png_dvipng, s, wrap)
50 yield (latextools.latex_to_png_dvipng, s, wrap)
51
51
52 @skipif_not_matplotlib
53 def test_latex_to_png_mpl_runs():
54 """
55 Test that latex_to_png_mpl just runs without error.
56 """
57 def mock_kpsewhich(filename):
58 nt.assert_equals(filename, "breqn.sty")
59 return None
60
61 for (s, wrap) in [("$x^2$", False), ("x^2", True)]:
62 yield (latextools.latex_to_png_mpl, s, wrap)
63
64 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
65 yield (latextools.latex_to_png_mpl, s, wrap)
66
67 @skipif_not_matplotlib
68 def test_latex_to_html():
69 img = latextools.latex_to_html("$x^2$")
70 nt.assert_in("data:image/png;base64,iVBOR", img)
71
52
72
53 def test_genelatex_no_wrap():
73 def test_genelatex_no_wrap():
54 """
74 """
55 Test genelatex with wrap=False.
75 Test genelatex with wrap=False.
56 """
76 """
57 def mock_kpsewhich(filename):
77 def mock_kpsewhich(filename):
58 assert False, ("kpsewhich should not be called "
78 assert False, ("kpsewhich should not be called "
59 "(called with {0})".format(filename))
79 "(called with {0})".format(filename))
60
80
61 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
81 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
62 nt.assert_equals(
82 nt.assert_equals(
63 '\n'.join(latextools.genelatex("body text", False)),
83 '\n'.join(latextools.genelatex("body text", False)),
64 r'''\documentclass{article}
84 r'''\documentclass{article}
65 \usepackage{amsmath}
85 \usepackage{amsmath}
66 \usepackage{amsthm}
86 \usepackage{amsthm}
67 \usepackage{amssymb}
87 \usepackage{amssymb}
68 \usepackage{bm}
88 \usepackage{bm}
69 \pagestyle{empty}
89 \pagestyle{empty}
70 \begin{document}
90 \begin{document}
71 body text
91 body text
72 \end{document}''')
92 \end{document}''')
73
93
74
94
75 def test_genelatex_wrap_with_breqn():
95 def test_genelatex_wrap_with_breqn():
76 """
96 """
77 Test genelatex with wrap=True for the case breqn.sty is installed.
97 Test genelatex with wrap=True for the case breqn.sty is installed.
78 """
98 """
79 def mock_kpsewhich(filename):
99 def mock_kpsewhich(filename):
80 nt.assert_equals(filename, "breqn.sty")
100 nt.assert_equals(filename, "breqn.sty")
81 return "path/to/breqn.sty"
101 return "path/to/breqn.sty"
82
102
83 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
103 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
84 nt.assert_equals(
104 nt.assert_equals(
85 '\n'.join(latextools.genelatex("x^2", True)),
105 '\n'.join(latextools.genelatex("x^2", True)),
86 r'''\documentclass{article}
106 r'''\documentclass{article}
87 \usepackage{amsmath}
107 \usepackage{amsmath}
88 \usepackage{amsthm}
108 \usepackage{amsthm}
89 \usepackage{amssymb}
109 \usepackage{amssymb}
90 \usepackage{bm}
110 \usepackage{bm}
91 \usepackage{breqn}
111 \usepackage{breqn}
92 \pagestyle{empty}
112 \pagestyle{empty}
93 \begin{document}
113 \begin{document}
94 \begin{dmath*}
114 \begin{dmath*}
95 x^2
115 x^2
96 \end{dmath*}
116 \end{dmath*}
97 \end{document}''')
117 \end{document}''')
98
118
99
119
100 def test_genelatex_wrap_without_breqn():
120 def test_genelatex_wrap_without_breqn():
101 """
121 """
102 Test genelatex with wrap=True for the case breqn.sty is not installed.
122 Test genelatex with wrap=True for the case breqn.sty is not installed.
103 """
123 """
104 def mock_kpsewhich(filename):
124 def mock_kpsewhich(filename):
105 nt.assert_equals(filename, "breqn.sty")
125 nt.assert_equals(filename, "breqn.sty")
106 return None
126 return None
107
127
108 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
128 with monkeypatch(latextools, "kpsewhich", mock_kpsewhich):
109 nt.assert_equals(
129 nt.assert_equals(
110 '\n'.join(latextools.genelatex("x^2", True)),
130 '\n'.join(latextools.genelatex("x^2", True)),
111 r'''\documentclass{article}
131 r'''\documentclass{article}
112 \usepackage{amsmath}
132 \usepackage{amsmath}
113 \usepackage{amsthm}
133 \usepackage{amsthm}
114 \usepackage{amssymb}
134 \usepackage{amssymb}
115 \usepackage{bm}
135 \usepackage{bm}
116 \pagestyle{empty}
136 \pagestyle{empty}
117 \begin{document}
137 \begin{document}
118 $$x^2$$
138 $$x^2$$
119 \end{document}''')
139 \end{document}''')
General Comments 0
You need to be logged in to leave comments. Login now