# -*- coding: utf-8 -*- """Tools for handling LaTeX. Authors: * Brian Granger """ #----------------------------------------------------------------------------- # Copyright (C) 2010 IPython Development Team. # # Distributed under the terms of the Modified BSD License. # # The full license is in the file COPYING.txt, distributed with this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- from StringIO import StringIO from base64 import encodestring import os import tempfile import shutil import subprocess from IPython.utils.process import find_cmd, FindCmdError #----------------------------------------------------------------------------- # Tools #----------------------------------------------------------------------------- def latex_to_png(s, encode=False, backend='mpl', wrap=False): """Render a LaTeX string to PNG. Parameters ---------- s : str The raw string containing valid inline LaTeX. encode : bool, optional Should the PNG data bebase64 encoded to make it JSON'able. backend : {mpl, dvipng} Backend for producing PNG data. wrap : bool If true, Automatically wrap `s` as a LaTeX equation. None is returned when the backend cannot be used. """ if backend == 'mpl': f = latex_to_png_mpl elif backend == 'dvipng': f = latex_to_png_dvipng else: raise ValueError('No such backend {0}'.format(backend)) bin_data = f(s, wrap) if encode and bin_data: bin_data = encodestring(bin_data) return bin_data def latex_to_png_mpl(s, wrap): try: from matplotlib import mathtext except ImportError: return None if wrap: s = '${0}$'.format(s) mt = mathtext.MathTextParser('bitmap') f = StringIO() mt.to_png(f, s, fontsize=12) return f.getvalue() def latex_to_png_dvipng(s, wrap): try: find_cmd('latex') find_cmd('dvipng') except FindCmdError: return None try: workdir = tempfile.mkdtemp() tmpfile = os.path.join(workdir, "tmp.tex") dvifile = os.path.join(workdir, "tmp.dvi") outfile = os.path.join(workdir, "tmp.png") with open(tmpfile, "w") as f: f.writelines(genelatex(s, wrap)) subprocess.check_call( ["latex", "-halt-on-errror", tmpfile], cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.check_call( ["dvipng", "-T", "tight", "-x", "1500", "-z", "9", "-bg", "transparent", "-o", outfile, dvifile], cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) with open(outfile) as f: bin_data = f.read() finally: shutil.rmtree(workdir) return bin_data def kpsewhich(filename): """Invoke kpsewhich command with an argument `filename`.""" try: find_cmd("kpsewhich") proc = subprocess.Popen( ["kpsewhich", filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = proc.communicate() return stdout.strip() except FindCmdError: pass def genelatex(body, wrap): """Generate LaTeX document for dvipng backend.""" breqn = wrap and kpsewhich("breqn.sty") yield r'\documentclass{article}' packages = ['amsmath', 'amsthm', 'amssymb', 'bm'] if breqn: packages.append('breqn') for pack in packages: yield r'\usepackage{{{0}}}'.format(pack) yield r'\pagestyle{empty}' yield r'\begin{document}' if breqn: yield r'\begin{dmath*}' yield body yield r'\end{dmath*}' elif wrap: yield '$${0}$$'.format(body) else: yield body yield r'\end{document}' _data_uri_template_png = """%s""" def latex_to_html(s, alt='image'): """Render LaTeX to HTML with embedded PNG data using data URIs. Parameters ---------- s : str The raw string containing valid inline LateX. alt : str The alt text to use for the HTML. """ base64_data = latex_to_png(s, encode=True) if base64_data: return _data_uri_template_png % (base64_data, alt) # From matplotlib, thanks to mdboom. Once this is in matplotlib releases, we # will remove. def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None): """ Given a math expression, renders it in a closely-clipped bounding box to an image file. *s* A math expression. The math portion should be enclosed in dollar signs. *filename_or_obj* A filepath or writable file-like object to write the image data to. *prop* If provided, a FontProperties() object describing the size and style of the text. *dpi* Override the output dpi, otherwise use the default associated with the output format. *format* The output format, eg. 'svg', 'pdf', 'ps' or 'png'. If not provided, will be deduced from the filename. """ from matplotlib import figure # backend_agg supports all of the core output formats from matplotlib.backends import backend_agg from matplotlib.font_manager import FontProperties from matplotlib.mathtext import MathTextParser if prop is None: prop = FontProperties() parser = MathTextParser('path') width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop) fig = figure.Figure(figsize=(width / 72.0, height / 72.0)) fig.text(0, depth/height, s, fontproperties=prop) backend_agg.FigureCanvasAgg(fig) fig.savefig(filename_or_obj, dpi=dpi, format=format) return depth