Source code for mlabtex.core

# -*- coding: utf-8 -*-
"""mlabtex: A latex renderer for mayavi."""
from __future__ import absolute_import, division, print_function

import os
import tempfile
import numpy as np
from mayavi import mlab
from tvtk.api import tvtk

# all supported image formates by tvtk
IMREAD = {
    "bmp": tvtk.BMPReader,
    "jpg": tvtk.JPEGReader,
    "jpeg": tvtk.JPEGReader,
    "png": tvtk.PNGReader,
    "pnm": tvtk.PNMReader,
    "dcm": tvtk.DICOMImageReader,
    "tiff": tvtk.TIFFReader,
    "ximg": tvtk.GESignaReader,
    "dem": tvtk.DEMReader,
    "mha": tvtk.MetaImageReader,
    "mhd": tvtk.MetaImageReader,
    "mnc": tvtk.MINCImageReader,
}


class RenderError(Exception):
    """Render error."""

    pass


class TmpFile(object):
    """
    A closed temporary file class.

    Attributes
    ----------
    name : string
        Name and path to the file.
    file : class
        Temporary file handler
    """

    def __init__(self, suffix="txt"):
        """
        A closed temporary file.

        Parameter
        ---------
        suffix : string, optional
            The suffix for the temporary file. Default: "txt"
        """
        self.file = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
        self.file.close()

    @property
    def name(self):
        """File name."""
        return self.file.name

    def close(self):
        """Unlink the file."""
        os.unlink(self.name)


def render_latex_mpl(text, path, color=(0, 0, 0), dpi=600, output="png"):
    r"""
    Render a LaTeX-formula into an image with matplotlib.

    Parameters
    ----------
    text : string
        String containing the latex-code.
    path : string
        Path to the file to be saved.
    color : tuple, optional
        color of the text given as rgb tuple. Default: ``(0, 0, 0)``
    dpi : int, optional
        Used dpi. Default: 1200
    output : string, optional
        Output format. Default: ``"png"``

    Notes
    -----
    If you get the following error:

        ``RuntimeError: libpng signaled error``

    Try to set the dpi higher. (1200 recomended)

    If big symbols like ``\int`` or ``\sum`` don't show up properly,
    try setting a

        ``\displaystyle``

    infront of them.
    """
    from matplotlib.mathtext import MathTextParser
    from matplotlib.font_manager import FontProperties
    from matplotlib import figure, rc

    # backend_agg supports all of the core output formats
    from matplotlib.backends import backend_agg

    rc("text", usetex=False)
    prop = FontProperties()
    parser = MathTextParser("path")
    width, height, depth, _, _ = parser.parse(text, dpi=72, prop=prop)
    fig = figure.Figure(figsize=(width / 72.0, height / 72.0))
    fig.text(0, depth / height, text, fontproperties=prop, color=color)
    backend_agg.FigureCanvasAgg(fig)
    fig.savefig(path, dpi=dpi, format=output, transparent=True)


def render_latex_sympy(text, path, color=(0, 0, 0), dpi=600, output="png"):
    r"""
    Renders LaTeX-formula into an image with sympy.

    Parameters
    ----------
    text : string
        String containing the latex-code.
    path : string
        Path to the file to be saved.
    color : tuple, optional
        color of the text given as rgb tuple. Default: ``(0, 0, 0)``
    dpi : int, optional
        Used dpi. Default: 1200
    output : string, optional
        Output format. Default: ``"png"``

    Notes
    -----
    If you get the following error:

        ``RuntimeError: libpng signaled error``

    Try to set the dpi higher. (1200 recomended)

    If big symbols like ``\int`` or ``\sum`` don't show up properly,
    try setting a

        ``\displaystyle``

    infront of them.
    """
    from sympy import preview

    preamble = (
        r"\documentclass[12pt]{article}"
        + os.linesep
        + r"\pagestyle{empty}"
        + os.linesep
        + r"\usepackage[utf8]{inputenc}"
        + os.linesep
        + r"\usepackage{amsmath}"
        + os.linesep
        + r"\usepackage{amssymb}"
        + os.linesep
        + r"\usepackage{amsfonts}"
        + os.linesep
        + r"\usepackage{helvet}"
        + os.linesep
        + r"\renewcommand{\familydefault}{\sfdefault}"
        + os.linesep
        + r"\usepackage{xcolor}"
        + os.linesep
        + r"\definecolor{user}{rgb}"
        + "{"
        + "{}, {}, {}".format(*color)
        + "}"
        + os.linesep
        + r"\color{user}"
        + os.linesep
        + r"\everymath{\displaystyle}"
        + os.linesep
        + r"\begin{document}"
    )
    preview(
        text,
        viewer="file",
        output=output,
        filename=path,
        preamble=preamble,
        euler=False,
        dvioptions=[
            "-T",
            "tight",
            "-z",
            "0",
            "--truecolor",
            "-D",
            str(int(dpi)),
            "-bg",
            "Transparent",
        ],
    )


[docs]def render_latex(text, path, color=(0, 0, 0), dpi=600, output="png"): r""" Renders LaTeX-formula into an image. Parameters ---------- text : string String containing the latex-code. path : string Path to the file to be saved. color : tuple, optional color of the text given as rgb tuple. Default: ``(0, 0, 0)`` dpi : int, optional Used dpi. Default: 1200 output : string, optional Output format. Default: ``"png"`` Notes ----- If you get the following error: ``RuntimeError: libpng signaled error`` Try to set the dpi higher. (1200 recomended) If big symbols like ``\int`` or ``\sum`` don't show up properly, try setting a ``\displaystyle`` infront of them. It will try to render it with sympy first. If that fails it will use matplotlib. """ try: render_latex_sympy(text, path, color, dpi, output) except Exception as exc_1: try: render_latex_mpl(text, path, color, dpi, output) except Exception as exc_2: raise RenderError( "Mlabtex: Could not render the latex-code..." + os.linesep + str(exc_1) + os.linesep + str(exc_2) + os.linesep )
[docs]def mlabimg( x, y, z, path, figure=None, name=None, opacity=1.0, orientation=(0.0, 0.0, 0.0), scale=1.0, typ=None, ref_y_extent=None, ): """ Render image files in mayavi. Analogous to mlab.text3d. Parameters ---------- x : float x position of the text. y : float y position of the text. z : float z position of the text. path : string Path to the image file. color : tuple, optional color of the text given as rgb tuple. Default: ``(0, 0, 0)`` figure : Scene, optional Must be a Scene or None. name : string, optional the name of the vtk object created. opacity : float, optional The overall opacity of the vtk object. Must be a float. Default: 1.0 orientation : tuple, optional the angles giving the orientation of the text. If the text is oriented to the camera, these angles are referenced to the axis of the camera. If not, these angles are referenced to the z axis. Must be an array with shape (3,). scale : float, optional The vetical scale of the image, in figure units. typ : string, optional Here you can specify the image type. Supported: 'bmp', 'jpg', 'jpeg', 'png', 'pnm', 'dcm', 'tiff', 'ximg', 'dem', 'mha', 'mhd', 'mnc'. If set to ``None``, the file type is determined by its extension. Default: None. ref_y_extent : int, optional Reference vertical extent of the image to scale to. If set to ``None``, the image extent itself is used. Default: None Returns ------- surf : Surf Mayavi ``Surf`` class with the rendered image as texture. """ if typ is None: typ = os.path.splitext(path)[1][1:].lower() if typ not in IMREAD: raise ValueError("The file type is not supported: " + str(typ)) reader = IMREAD[typ] kwargs = {} if figure is not None: kwargs["figure"] = figure if name is not None: kwargs["name"] = name # load the image img = reader() img.file_name = path img.update() dim_x, dim_y = img.data_extent[1:4:2] # create the texture from the image texture = tvtk.Texture(input_connection=img.output_port, interpolate=0) # create the surface points if ref_y_extent is None: ref_y_extent = dim_y surfx, surfy = ( np.mgrid[0 : dim_x + 1, 0 : dim_y + 1] * scale / ref_y_extent ) surfz = np.zeros_like(surfx) # create the surface surf = mlab.surf( surfx, surfy, surfz, color=(1, 1, 1), opacity=opacity, warp_scale=1.0, reset_zoom=False, **kwargs ) # add texture, position and orientation surf.actor.enable_texture = True surf.actor.tcoord_generator_mode = "plane" surf.actor.actor.texture = texture surf.actor.actor.orientation = orientation surf.actor.actor.position = (x, y, z) return surf
[docs]def mlabtex( x, y, z, text, color=(0, 0, 0), figure=None, name=None, opacity=1.0, orientation=(0.0, 0.0, 0.0), scale=1.0, dpi=1200, ): r""" Render for matplotlib like text in mayavi. Analogous to mlab.text3d. Parameters ---------- x : float x position of the text. y : float y position of the text. z : float z position of the text. text : string The text is positionned in 3D, in figure coordinnates. color : tuple, optional color of the text given as rgb tuple. Default: ``(0, 0, 0)`` figure : Scene, optional Must be a Scene or None. name : string, optional the name of the vtk object created. opacity : float, optional The overall opacity of the vtk object. Must be a float. Default: 1.0 orientation : tuple, optional the angles giving the orientation of the text. If the text is oriented to the camera, these angles are referenced to the axis of the camera. If not, these angles are referenced to the z axis. Must be an array with shape (3,). scale : float, optional The scale of the text, in figure units. It is rescaled by the size of the letter "I". dpi : int, optional Used dpi. Default: 1200 Returns ------- surf : Surf Mayavi ``Surf`` class with the rendered text as texture. Notes ----- If you get the following error: ``RuntimeError: libpng signaled error`` Try to set the dpi higher. (1200 recomended) If big symbols like ``\int`` or ``\sum`` don't show up properly, try setting a ``\displaystyle`` infront of them. """ # create temporary file for the reference height of the letter "I" reffile = TmpFile(suffix=".png") render_latex(r"I", path=reffile.name, dpi=dpi) ref = tvtk.PNGReader() ref.file_name = reffile.name ref.update() # Reference heigth of the letter "I" ref_y = ref.data_extent[3] reffile.close() # create temporary file for the png pngfile = TmpFile(suffix=".png") # create png file containing text with matplotlib render_latex(text, path=pngfile.name, color=color, dpi=dpi) # loading image with tvtk surf = mlabimg( x, y, z, pngfile.name, figure, name, opacity, orientation, scale, typ="png", ref_y_extent=ref_y, ) # close temp file pngfile.close() return surf