##// END OF EJS Templates
Merge pull request #1861 from tkf/dvipng...
Fernando Perez -
r7409:7cd55e57 merge
parent child Browse files
Show More
@@ -1,133 +1,151
1 """
1 """
2 A print function that pretty prints sympy Basic objects.
2 A print function that pretty prints sympy Basic objects.
3
3
4 :moduleauthor: Brian Granger
4 :moduleauthor: Brian Granger
5
5
6 Usage
6 Usage
7 =====
7 =====
8
8
9 Once the extension is loaded, Sympy Basic objects are automatically
9 Once the extension is loaded, Sympy Basic objects are automatically
10 pretty-printed.
10 pretty-printed.
11
11
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 from IPython.lib.latextools import latex_to_png
24 from IPython.lib.latextools import latex_to_png
25 from IPython.testing import decorators as dec
25 from IPython.testing import decorators as dec
26 # use @dec.skipif_not_sympy to skip tests requiring sympy
26 # use @dec.skipif_not_sympy to skip tests requiring sympy
27
27
28 try:
28 try:
29 from sympy import pretty, latex
29 from sympy import pretty, latex
30 except ImportError:
30 except ImportError:
31 pass
31 pass
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Definitions of special display functions for use with IPython
34 # Definitions of special display functions for use with IPython
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 def print_basic_unicode(o, p, cycle):
37 def print_basic_unicode(o, p, cycle):
38 """A function to pretty print sympy Basic objects."""
38 """A function to pretty print sympy Basic objects."""
39 if cycle:
39 if cycle:
40 return p.text('Basic(...)')
40 return p.text('Basic(...)')
41 out = pretty(o, use_unicode=True)
41 out = pretty(o, use_unicode=True)
42 if '\n' in out:
42 if '\n' in out:
43 p.text(u'\n')
43 p.text(u'\n')
44 p.text(out)
44 p.text(out)
45
45
46
46
47 def print_png(o):
47 def print_png(o):
48 """A function to display sympy expression using LaTex -> PNG."""
48 """
49 A function to display sympy expression using inline style LaTeX in PNG.
50 """
49 s = latex(o, mode='inline')
51 s = latex(o, mode='inline')
50 # mathtext does not understand certain latex flags, so we try to replace
52 # mathtext does not understand certain latex flags, so we try to replace
51 # them with suitable subs.
53 # them with suitable subs.
52 s = s.replace('\\operatorname','')
54 s = s.replace('\\operatorname','')
53 s = s.replace('\\overline', '\\bar')
55 s = s.replace('\\overline', '\\bar')
54 png = latex_to_png(s)
56 png = latex_to_png(s)
55 return png
57 return png
56
58
59
60 def print_display_png(o):
61 """
62 A function to display sympy expression using display style LaTeX in PNG.
63 """
64 s = latex(o, mode='plain')
65 s = s.strip('$')
66 # As matplotlib does not support display style, dvipng backend is
67 # used here.
68 png = latex_to_png('$$%s$$' % s, backend='dvipng')
69 return png
70
71
57 def can_print_latex(o):
72 def can_print_latex(o):
58 """
73 """
59 Return True if type o can be printed with LaTeX.
74 Return True if type o can be printed with LaTeX.
60
75
61 If o is a container type, this is True if and only if every element of o
76 If o is a container type, this is True if and only if every element of o
62 can be printed with LaTeX.
77 can be printed with LaTeX.
63 """
78 """
64 import sympy
79 import sympy
65 if isinstance(o, (list, tuple, set, frozenset)):
80 if isinstance(o, (list, tuple, set, frozenset)):
66 return all(can_print_latex(i) for i in o)
81 return all(can_print_latex(i) for i in o)
67 elif isinstance(o, dict):
82 elif isinstance(o, dict):
68 return all((isinstance(i, basestring) or can_print_latex(i)) and can_print_latex(o[i]) for i in o)
83 return all((isinstance(i, basestring) or can_print_latex(i)) and can_print_latex(o[i]) for i in o)
69 elif isinstance(o,(sympy.Basic, sympy.matrices.Matrix, int, long, float)):
84 elif isinstance(o,(sympy.Basic, sympy.matrices.Matrix, int, long, float)):
70 return True
85 return True
71 return False
86 return False
72
87
73 def print_latex(o):
88 def print_latex(o):
74 """A function to generate the latex representation of sympy
89 """A function to generate the latex representation of sympy
75 expressions."""
90 expressions."""
76 if can_print_latex(o):
91 if can_print_latex(o):
77 s = latex(o, mode='plain')
92 s = latex(o, mode='plain')
78 s = s.replace('\\dag','\\dagger')
93 s = s.replace('\\dag','\\dagger')
79 s = s.strip('$')
94 s = s.strip('$')
80 return '$$%s$$' % s
95 return '$$%s$$' % s
81 # Fallback to the string printer
96 # Fallback to the string printer
82 return None
97 return None
83
98
84 _loaded = False
99 _loaded = False
85
100
86 def load_ipython_extension(ip):
101 def load_ipython_extension(ip):
87 """Load the extension in IPython."""
102 """Load the extension in IPython."""
88 import sympy
103 import sympy
89 global _loaded
104 global _loaded
90 if not _loaded:
105 if not _loaded:
91 plaintext_formatter = ip.display_formatter.formatters['text/plain']
106 plaintext_formatter = ip.display_formatter.formatters['text/plain']
92
107
93 for cls in (object, str):
108 for cls in (object, str):
94 plaintext_formatter.for_type(cls, print_basic_unicode)
109 plaintext_formatter.for_type(cls, print_basic_unicode)
95
110
96 printable_containers = [list, tuple]
111 printable_containers = [list, tuple]
97
112
98 # set and frozen set were broken with SymPy's latex() function, but
113 # set and frozen set were broken with SymPy's latex() function, but
99 # was fixed in the 0.7.1-git development version. See
114 # was fixed in the 0.7.1-git development version. See
100 # http://code.google.com/p/sympy/issues/detail?id=3062.
115 # http://code.google.com/p/sympy/issues/detail?id=3062.
101 if sympy.__version__ > '0.7.1':
116 if sympy.__version__ > '0.7.1':
102 printable_containers += [set, frozenset]
117 printable_containers += [set, frozenset]
103 else:
118 else:
104 plaintext_formatter.for_type(cls, print_basic_unicode)
119 plaintext_formatter.for_type(cls, print_basic_unicode)
105
120
106 plaintext_formatter.for_type_by_name(
121 plaintext_formatter.for_type_by_name(
107 'sympy.core.basic', 'Basic', print_basic_unicode
122 'sympy.core.basic', 'Basic', print_basic_unicode
108 )
123 )
109 plaintext_formatter.for_type_by_name(
124 plaintext_formatter.for_type_by_name(
110 'sympy.matrices.matrices', 'Matrix', print_basic_unicode
125 'sympy.matrices.matrices', 'Matrix', print_basic_unicode
111 )
126 )
112
127
113 png_formatter = ip.display_formatter.formatters['image/png']
128 png_formatter = ip.display_formatter.formatters['image/png']
114
129
115 png_formatter.for_type_by_name(
130 png_formatter.for_type_by_name(
116 'sympy.core.basic', 'Basic', print_png
131 'sympy.core.basic', 'Basic', print_png
117 )
132 )
133 png_formatter.for_type_by_name(
134 'sympy.matrices.matrices', 'Matrix', print_display_png
135 )
118 for cls in [dict, int, long, float] + printable_containers:
136 for cls in [dict, int, long, float] + printable_containers:
119 png_formatter.for_type(cls, print_png)
137 png_formatter.for_type(cls, print_png)
120
138
121 latex_formatter = ip.display_formatter.formatters['text/latex']
139 latex_formatter = ip.display_formatter.formatters['text/latex']
122 latex_formatter.for_type_by_name(
140 latex_formatter.for_type_by_name(
123 'sympy.core.basic', 'Basic', print_latex
141 'sympy.core.basic', 'Basic', print_latex
124 )
142 )
125 latex_formatter.for_type_by_name(
143 latex_formatter.for_type_by_name(
126 'sympy.matrices.matrices', 'Matrix', print_latex
144 'sympy.matrices.matrices', 'Matrix', print_latex
127 )
145 )
128
146
129 for cls in printable_containers:
147 for cls in printable_containers:
130 # Use LaTeX only if every element is printable by latex
148 # Use LaTeX only if every element is printable by latex
131 latex_formatter.for_type(cls, print_latex)
149 latex_formatter.for_type(cls, print_latex)
132
150
133 _loaded = True
151 _loaded = True
@@ -1,110 +1,181
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-2011, 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
23 import tempfile
24 import shutil
25 import subprocess
26
27 from IPython.utils.process import find_cmd, FindCmdError
22
28
23 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
24 # Tools
30 # Tools
25 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
26
32
27
33
28 def latex_to_png(s, encode=False):
34 def latex_to_png(s, encode=False, backend='mpl'):
29 """Render a LaTeX string to PNG using matplotlib.mathtext.
35 """Render a LaTeX string to PNG.
30
36
31 Parameters
37 Parameters
32 ----------
38 ----------
33 s : str
39 s : str
34 The raw string containing valid inline LaTeX.
40 The raw string containing valid inline LaTeX.
35 encode : bool, optional
41 encode : bool, optional
36 Should the PNG data bebase64 encoded to make it JSON'able.
42 Should the PNG data bebase64 encoded to make it JSON'able.
43 backend : {mpl, dvipng}
44 Backend for producing PNG data.
45
46 None is returned when the backend cannot be used.
47
37 """
48 """
49 if backend == 'mpl':
50 f = latex_to_png_mpl
51 elif backend == 'dvipng':
52 f = latex_to_png_dvipng
53 else:
54 raise ValueError('No such backend {0}'.format(backend))
55 bin_data = f(s)
56 if encode and bin_data:
57 bin_data = encodestring(bin_data)
58 return bin_data
59
60
61 def latex_to_png_mpl(s):
62 try:
38 from matplotlib import mathtext
63 from matplotlib import mathtext
64 except ImportError:
65 return None
39
66
40 mt = mathtext.MathTextParser('bitmap')
67 mt = mathtext.MathTextParser('bitmap')
41 f = StringIO()
68 f = StringIO()
42 mt.to_png(f, s, fontsize=12)
69 mt.to_png(f, s, fontsize=12)
43 bin_data = f.getvalue()
70 return f.getvalue()
44 if encode:
71
45 bin_data = encodestring(bin_data)
72
73 def latex_to_png_dvipng(s):
74 try:
75 find_cmd('latex')
76 find_cmd('dvipng')
77 except FindCmdError:
78 return None
79 try:
80 workdir = tempfile.mkdtemp()
81 tmpfile = os.path.join(workdir, "tmp.tex")
82 dvifile = os.path.join(workdir, "tmp.dvi")
83 outfile = os.path.join(workdir, "tmp.png")
84
85 with open(tmpfile, "w") as f:
86 f.write(_latex_header)
87 f.write(s)
88 f.write(_latex_footer)
89
90 subprocess.check_call(
91 ["latex", "-halt-on-errror", tmpfile], cwd=workdir,
92 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
93
94 subprocess.check_call(
95 ["dvipng", "-T", "tight", "-x", "1500", "-z", "9",
96 "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir,
97 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
98
99 with open(outfile) as f:
100 bin_data = f.read()
101 finally:
102 shutil.rmtree(workdir)
46 return bin_data
103 return bin_data
47
104
48
105
106 _latex_header = r'''
107 \documentclass{article}
108 \usepackage{amsmath}
109 \usepackage{amsthm}
110 \usepackage{amssymb}
111 \usepackage{bm}
112 \pagestyle{empty}
113 \begin{document}
114 '''
115
116 _latex_footer = r'\end{document}'
117
118
49 _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 />"""
50
120
51 def latex_to_html(s, alt='image'):
121 def latex_to_html(s, alt='image'):
52 """Render LaTeX to HTML with embedded PNG data using data URIs.
122 """Render LaTeX to HTML with embedded PNG data using data URIs.
53
123
54 Parameters
124 Parameters
55 ----------
125 ----------
56 s : str
126 s : str
57 The raw string containing valid inline LateX.
127 The raw string containing valid inline LateX.
58 alt : str
128 alt : str
59 The alt text to use for the HTML.
129 The alt text to use for the HTML.
60 """
130 """
61 base64_data = latex_to_png(s, encode=True)
131 base64_data = latex_to_png(s, encode=True)
132 if base64_data:
62 return _data_uri_template_png % (base64_data, alt)
133 return _data_uri_template_png % (base64_data, alt)
63
134
64
135
65 # 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
66 # will remove.
137 # will remove.
67 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):
68 """
139 """
69 Given a math expression, renders it in a closely-clipped bounding
140 Given a math expression, renders it in a closely-clipped bounding
70 box to an image file.
141 box to an image file.
71
142
72 *s*
143 *s*
73 A math expression. The math portion should be enclosed in
144 A math expression. The math portion should be enclosed in
74 dollar signs.
145 dollar signs.
75
146
76 *filename_or_obj*
147 *filename_or_obj*
77 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
78 to.
149 to.
79
150
80 *prop*
151 *prop*
81 If provided, a FontProperties() object describing the size and
152 If provided, a FontProperties() object describing the size and
82 style of the text.
153 style of the text.
83
154
84 *dpi*
155 *dpi*
85 Override the output dpi, otherwise use the default associated
156 Override the output dpi, otherwise use the default associated
86 with the output format.
157 with the output format.
87
158
88 *format*
159 *format*
89 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
160 The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not
90 provided, will be deduced from the filename.
161 provided, will be deduced from the filename.
91 """
162 """
92 from matplotlib import figure
163 from matplotlib import figure
93 # backend_agg supports all of the core output formats
164 # backend_agg supports all of the core output formats
94 from matplotlib.backends import backend_agg
165 from matplotlib.backends import backend_agg
95 from matplotlib.font_manager import FontProperties
166 from matplotlib.font_manager import FontProperties
96 from matplotlib.mathtext import MathTextParser
167 from matplotlib.mathtext import MathTextParser
97
168
98 if prop is None:
169 if prop is None:
99 prop = FontProperties()
170 prop = FontProperties()
100
171
101 parser = MathTextParser('path')
172 parser = MathTextParser('path')
102 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
173 width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop)
103
174
104 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
175 fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
105 fig.text(0, depth/height, s, fontproperties=prop)
176 fig.text(0, depth/height, s, fontproperties=prop)
106 backend_agg.FigureCanvasAgg(fig)
177 backend_agg.FigureCanvasAgg(fig)
107 fig.savefig(filename_or_obj, dpi=dpi, format=format)
178 fig.savefig(filename_or_obj, dpi=dpi, format=format)
108
179
109 return depth
180 return depth
110
181
General Comments 0
You need to be logged in to leave comments. Login now