diff --git a/IPython/lib/latextools.py b/IPython/lib/latextools.py index 0acd16d..ace6fdf 100644 --- a/IPython/lib/latextools.py +++ b/IPython/lib/latextools.py @@ -10,6 +10,7 @@ import tempfile import shutil import subprocess from base64 import encodebytes +from textwrap import wrap as splitstring from IPython.utils.process import find_cmd, FindCmdError from traitlets.config import get_config @@ -55,7 +56,8 @@ class LaTeXTool(SingletonConfigurable): ).tag(config=True) -def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black'): +def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black', + scale=1.0): """Render a LaTeX string to PNG. Parameters @@ -69,7 +71,10 @@ def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black'): wrap : bool If true, Automatically wrap `s` as a LaTeX equation. color : string - Foreground color name among dvipsnames. + Foreground color name among dvipsnames, e.g. 'Maroon' or on hex RGB + format, e.g. '#AA20FA'. + scale : float + Scale factor for the resulting PNG. None is returned when the backend cannot be used. @@ -84,15 +89,25 @@ def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black'): f = latex_to_png_mpl elif backend == 'dvipng': f = latex_to_png_dvipng + if color.startswith('#'): + # Convert hex RGB color to LaTeX RGB color. + if len(color) == 7: + try: + color = "RGB {}".format(" ".join([str(int(x, 16)) for x in + splitstring(color[1:], 2)])) + except ValueError: + raise ValueError('Invalid color specification {}.'.format(color)) + else: + raise ValueError('Invalid color specification {}.'.format(color)) else: raise ValueError('No such backend {0}'.format(backend)) - bin_data = f(s, wrap, color) + bin_data = f(s, wrap, color, scale) if encode and bin_data: bin_data = encodebytes(bin_data) return bin_data -def latex_to_png_mpl(s, wrap, color='Black'): +def latex_to_png_mpl(s, wrap, color='Black', scale=1.0): try: from matplotlib import mathtext from pyparsing import ParseFatalException @@ -107,13 +122,14 @@ def latex_to_png_mpl(s, wrap, color='Black'): try: mt = mathtext.MathTextParser('bitmap') f = BytesIO() - mt.to_png(f, s, fontsize=12, color=color) + dpi = 120*scale + mt.to_png(f, s, fontsize=12, dpi=dpi, color=color) return f.getvalue() except (ValueError, RuntimeError, ParseFatalException): return None -def latex_to_png_dvipng(s, wrap, color='Black'): +def latex_to_png_dvipng(s, wrap, color='Black', scale=1.0): try: find_cmd('latex') find_cmd('dvipng') @@ -133,8 +149,9 @@ def latex_to_png_dvipng(s, wrap, color='Black'): ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile], cwd=workdir, stdout=devnull, stderr=devnull) + resolution = round(150*scale) subprocess.check_call( - ["dvipng", "-T", "tight", "-x", "1500", "-z", "9", + ["dvipng", "-T", "tight", "-D", str(resolution), "-z", "9", "-bg", "transparent", "-o", outfile, dvifile, "-fg", color], cwd=workdir, stdout=devnull, stderr=devnull) diff --git a/IPython/lib/tests/test_latextools.py b/IPython/lib/tests/test_latextools.py index 35e5309..2cbdb17 100644 --- a/IPython/lib/tests/test_latextools.py +++ b/IPython/lib/tests/test_latextools.py @@ -134,12 +134,16 @@ $$x^2$$ \end{document}''') +@skipif_not_matplotlib +@onlyif_cmds_exist('latex', 'dvipng') def test_latex_to_png_color(): """ Test color settings for latex_to_png. """ latex_string = "$x^2$" default_value = latextools.latex_to_png(latex_string, wrap=False) + default_hexblack = latextools.latex_to_png(latex_string, wrap=False, + color='#000000') dvipng_default = latextools.latex_to_png_dvipng(latex_string, False) dvipng_black = latextools.latex_to_png_dvipng(latex_string, False, 'Black') nt.assert_equal(dvipng_default, dvipng_black) @@ -147,11 +151,31 @@ def test_latex_to_png_color(): mpl_black = latextools.latex_to_png_mpl(latex_string, False, 'Black') nt.assert_equal(mpl_default, mpl_black) nt.assert_in(default_value, [dvipng_black, mpl_black]) + nt.assert_in(default_hexblack, [dvipng_black, mpl_black]) # Test that dvips name colors can be used without error - dvipng_maroon = latextools.latex_to_png_dvipng(latex_string, False, 'Maroon') + dvipng_maroon = latextools.latex_to_png_dvipng(latex_string, False, + 'Maroon') # And that it doesn't return the black one nt.assert_not_equal(dvipng_black, dvipng_maroon) mpl_maroon = latextools.latex_to_png_mpl(latex_string, False, 'Maroon') nt.assert_not_equal(mpl_black, mpl_maroon) + mpl_white = latextools.latex_to_png_mpl(latex_string, False, 'White') + mpl_hexwhite = latextools.latex_to_png_mpl(latex_string, False, '#FFFFFF') + nt.assert_equal(mpl_white, mpl_hexwhite) + + mpl_white_scale = latextools.latex_to_png_mpl(latex_string, False, + 'White', 1.2) + nt.assert_not_equal(mpl_white, mpl_white_scale) + + +def test_latex_to_png_invalid_hex_colors(): + """ + Test that invalid hex colors provided to dvipng gives an exception. + """ + latex_string = "$x^2$" + nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string, + backend='dvipng', color="#f00bar")) + nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string, + backend='dvipng', color="#f00"))