# TeX rendering in a Java application

I want to render math formulas in my Java GUI application. I am looking for a library made for computer use, not web use, which can render TeX. I have already tried jLatexMath, but it's very slow when I use it in my application.

If there are any other/better, could you point me in the right direction?

• I can think of three libraries that might somehow help or inspire you: JEuclid, SnuggleTeX and JLaTeXMath. – Paulo Cereda Jan 19 '12 at 16:42
• I am using jLatexMath in one of my own projects, and the first time I got it to display equations I also thought it was very slow, until I realized this was only true for the first invocation of jLatexMath. For every start of your application, the first rendering will be slow but subsequent renderings are fast. – propaganda Jan 20 '12 at 3:33

Friendly note: most of the conversion process involves input parsing, evaluation, font loading, rendering and output so that I wouldn't expect a nearly real-time conversion.

That said, let's go to business. :)

Warning: boring non-TeX technical Java stuff ahead. :)

I had a quick look at both SnuggleTeX and JEuclid to come up with this answer. Sorry, I didn't have time to come up with a better example.

The first one, SnuggleTeX, is described as "a free and open-source Java library for converting fragments of LaTeX to XML (usually XHTML + MathML)." The second one, JEuclid, is described as "a complete MathML rendering solution." What I actually did was to redirect one's output to the other's input.

First, with SnuggleTeX, you can obtain the needed code from the minimal example in its own homepage:

/* Create vanilla SnuggleEngine and new SnuggleSession */
SnuggleEngine engine = new SnuggleEngine();
SnuggleSession session = engine.createSession();

/* Parse some very basic Math Mode input */
SnuggleInput input = new SnuggleInput("$$x+2=3$$");
session.parseInput(input);

/* Convert the results to an XML String, which in this case will
* be a single MathML $...$ element. */
String xmlString = session.buildXMLString();


Now you have the MathML representation of your LaTeX input. Let's check JEuclid, from its API, and there's the Converter class with the following method:

BufferedImage render(Node node, LayoutContext context)


Then you can use net.sourceforge.jeuclid.MathMLParserSupport to parse your XML string to org.w3c.dom.Document. Calling the render method with the correct parameters will give you a BufferedImage representing your input.

My attempt:

It took around 1.4 secs to render this image.

I didn't like the output, but to be honest, I just wrote this app in 2 minutes as a [cough... cough... bad...] proof of concept. :) I'm almost sure the rendering quality can be improved, but I'm quite busy right now. Anyway, I think you can try something similar and then decide if this approach is worth a shot. :)

Update: It seems JEuclid has a JMathComponent in order to display MathML content in a Component.

• What is the minimum library to include to go from the MathML output to a PNG image? I don't require any other conversions and would like to keep the software as light as possible. So for just converting MathML to an image file using JEuclid do I just need to include the core and nothing else? – user32313 Jun 16 '13 at 6:10
• @Ozzy: according to JEuclid's usage page, the minimal distribution covers .png output, so it's certainly safe to use it. :) – Paulo Cereda Jun 16 '13 at 15:19

Another solution is to call mimetex.cgi (available here: http://www.forkosh.com/mimetex.html) from Java.

I do not pretend that this solution is "better" than the ones previously given. The purpose is just to give alternatives.

Example of result:

import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import javax.swing.*;

public class Exemple106_MimetexInterface {

private static String MIMETEX_EXE = "c:\\Program Files (x86)\\mimetex\\mimetex.cgi";

final private static int BUFFER_SIZE = 1024;

/**
* Convert LaTeX code to GIF
*
* @param latexString LaTeX code
* @return GIF image, under byte[] format
*/
public static byte[] getLaTeXImage(String latexString) {
byte[] imageData = null;
try {
// mimetex is asked (on the command line) to convert
// the LaTeX expression to .gif on standard output:
Process proc = Runtime.getRuntime().exec(MIMETEX_EXE + " -d \"" + latexString + "\"");
// get the output stream of the process:
BufferedInputStream bis = (BufferedInputStream) proc.getInputStream();
// read output process by bytes blocks (size: BUFFER_SIZE)
// and stores the result in a byte[] Arraylist:
byte[] buffer = new byte[BUFFER_SIZE];
ArrayList<byte[]> al = new ArrayList<byte[]>();
}
// convert the Arraylist in an unique array:
int nbOfArrays = al.size();
if (nbOfArrays == 1) {
imageData = buffer;
} else {
imageData = new byte[BUFFER_SIZE * nbOfArrays];
byte[] temp;
for (int k = 0; k < nbOfArrays; k++) {
temp = al.get(k);
for (int i = 0; i < BUFFER_SIZE; i++) {
imageData[BUFFER_SIZE * k + i] = temp[i];
}
}
}
bis.close();
proc.destroy();
} catch (IOException e) {
e.printStackTrace();
}
return imageData;
}

/**
* demonstration main
*
* @param args command line arguments
*/
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setLayout(new BorderLayout());

String LATEX_EXPRESSION_1 = "4$A=\$$\\array{3,c.cccBCCC&1&2&3\\\\\\hdash~1&a_{11}&a_{12}&a_{13}\\\\2&a_{21}&a_{22}&a_{23}\\\\3&a_{31}&a_{32}&a_{33}}\$$ "; byte[] imageData1 = getLaTeXImage(LATEX_EXPRESSION_1); JLabel button1 = new JLabel(new ImageIcon(imageData1)); jframe.add(button1, BorderLayout.NORTH); String LATEX_EXPRESSION_2 = "4$\\array{rccclBCB$&f&\\longr[75]^{\\alpha:{-1$f\\rightar~g}}&g\\\\3$\\gamma&\\longd[50]&&\\longd[50]&3$\\gamma\\\\&u&\\longr[75]_\\beta&v}";
byte[] imageData2 = getLaTeXImage(LATEX_EXPRESSION_2);
JLabel button2 = new JLabel(new ImageIcon(imageData2));

String LATEX_EXPRESSION_3 = "4$\\hspace{5}\\unitlength{1}\\picture(175,100){~(50,50){\\circle(100)}(1,50){\\overbrace{\\line(46)}^{4$\\;\\;a}}(52,50) {\\line(125)}~(50,52;115;2){\\mid}~(52,55){\\longleftar[60]}(130,56){\\longrightar[35]}~(116,58){r}~(c85,50;80;2){\\bullet} (c85,36){3$-q}~(c165,36){3$q}(42,30){\\underbrace{\\line(32)}_{1$a^2/r\\;\\;\\;}}~}"; byte[] imageData3 = getLaTeXImage(LATEX_EXPRESSION_3); JLabel button3 = new JLabel(new ImageIcon(imageData3)); jframe.add(button3, BorderLayout.SOUTH); jframe.pack(); jframe.setLocationRelativeTo(null); jframe.setVisible(true); } }  Nicolas For a limited subset of math-oriented plain TeX macros, I've forked JMathTeX to produce a version that can convert 1,000 simple TeX formulas into SVG documents in about 500 milliseconds on modern hardware, using only a couple of third-party Java libraries. The code is 100% pure Java: no external programs or web services are required to generate formulas. Here's a screenshot of my desktop application that integrates the JMathTeX fork: Here's a sample program that demonstrates using the API; the program exports equations to the system's temporary directory as SVG files: public class FormulaTest { private static final String DIR_TEMP = getProperty( "java.io.tmpdir" ); private final static String[] EQUATIONS = { "(a+b)^2=a^2 + 2ab + b^2", "S_x = sqrt((SS_x)/(N-1))", "e^{\\pi i} + 1 = 0", "\\sigma=\\sqrt{\\sum_{i=1}^{k} p_i(x_i-\\mu)^2}", "\\sqrt[n]{\\pi}", "\\sqrt[n]{|z| . e^{i \\theta}} = " + "\\sqrt[n]{|z| . e^{i (\\frac{\\theta + 2 k \\pi}{n})}}," + " k \\in \\lbrace 0, ..., n-1 \\rbrace, n \\in NN", "\\vec{u}^2 \\tilde{\\nu}", "\\sum_{i=1}^n i = (\\sum_{i=1}^{n-1} i) + n =\n" + "\\frac{(n-1)(n)}{2} + n = \\frac{n(n+1)}{2}", "\\int_{a}^{b} x^2 dx", "G_{\\mu \\nu} = \\frac{8 \\pi G}{c^4} T_{{\\mu \\nu}}", "\\prod_{i=a}^{b} f(i)", "u(n) \\Leftrightarrow \\frac{1}{1-e^{-jw}} + " + "\\sum_{k=-\\infty}^{\\infty} \\pi \\delta (\\omega + 2\\pi k)\n" }; public void test_Parser_SimpleFormulas_GeneratesSvg() throws IOException { final var size = 100f; final var texFont = new DefaultTeXFont( size ); final var env = new TeXEnvironment( texFont ); final var g = new SvgGraphics2D(); g.scale( size, size ); for( int j = 0; j < EQUATIONS.length; j++ ) { final var formula = new TeXFormula( EQUATIONS[ j ] ); final var box = formula.createBox( env ); final var layout = new TeXLayout( box, size ); g.initialize( layout.getWidth(), layout.getHeight() ); box.draw( g, layout.getX(), layout.getY() ); final var path = Path.of( DIR_TEMP, format( "eq-%02d.svg", j ) ); try( final var fos = new FileOutputStream( path.toFile() ); final var out = new OutputStreamWriter( fos, UTF_8 ) ) { out.write( g.toString() ); } } } public static void main( String[] args ) throws IOException { final var test = new FormulaTest(); test.test_Parser_SimpleFormulas_GeneratesSvg(); } }  Getting real-time rendering required the following core changes: • Replace Batik's SVGGraphics2D with a tailored version based on JFreeSVG. This produces SVG strings about three times faster than the fastest Graphics2D-to-SVG converter available. The JFreeSVG version does not reuse an internal StringBuilder; creating new string objects for the SVG content introduces more latency than appending to the same pre-sized buffer. • Change TeXFormula's parser to use conditional logic instead of throwing exceptions for flow control. This avoids filling out a stack trace for every character in a macro/command name until that name matches a known command. Instead, the command is first parsed and then looked up in a map. • Replace double-to-string conversion with the Ryu algorithm. This gave an immediate doubling of efficiency in SVG document creation; the hot spot for generating SVG is converting font glyphs path values into strings. The Ryu algorithm is the fastest known procedure for decimal-to-string conversion as of 2018. There were numerous other micro-optimizations made, but those items listed were the lion's share of the speed up. FWIW, JLaTeXMath has undergone a massive rewrite of its parser, as well, to address performance. Another solution to create a PNG image from LaTeX provided that LaTeX (for instance: MiKTeX) is installed on the computer... LaTeX standalone package allows the creation of a PNG output file, the size of which exactly corresponds to the size of the formula or of the text. So, we just have to call LaTeX from Java, and get the PNG output file. 1. Prerequisites a) LaTeX shall be installed on the computer ... with all packages necessary for the formula (in the below example: amsfonts and amsmath) ... with standalone package GhostScript shall be installed (necessary for standalone package) The directory containing gswin32c.exe shall be added to PATH. On my computer: C:\Program Files (x86)\gs\gs9.10\bin ImageMagick shall be installed (necessary for standalone package) convert.exe shall be renamed as imgconvert.exe The directory containing imgconvert.exe shall be added to PATH. On my computer: C:\Program Files (x86)\ImageMagick-6.8.8-9 2. Check that LaTeX (with standalone package) succeeds in generating the PNG file (no Java at this stage). LaTeX file, called New21.tex (for instance): \documentclass[border=0.50001bp,convert={convertexe={imgconvert},outext=.png}]{standalone} \usepackage{amsfonts} \usepackage{amsmath} \begin{document}$\begin{array}{l}

\forall\varepsilon\in\mathbb{R}_+^*\ \exists\eta>0\ |x-x_0|\leq\eta\Longrightarrow|f(x)-f(x_0)|\leq\varepsilon\\

\det\begin{bmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&\ddots&&\vdots\\\vdots&&\ddots&\vdots\\a_{n1}&\cdots&\cdots&a_{nn}\end{bmatrix}\overset{\mathrm{def}}{=}\sum_{\sigma\in\mathfrak{S}_n}\varepsilon(\sigma)\prod_{k=1}^n a_{k\sigma(k)}\\

{\sideset{_\alpha^\beta}{_\gamma^\delta}{\mathop{\begin{pmatrix}a&b\\c&d\end{pmatrix}}}}\\

\int_0^\infty{x^{2n} e^{-a x^2}\,dx} = \frac{2n-1}{2a} \int_0^\infty{x^{2(n-1)} e^{-a x^2}\,dx} = \frac{(2n-1)!!}{2^{n+1}} \sqrt{\frac{\pi}{a^{2n+1}}}\\

\int_a^b{f(x)\,dx} = (b - a) \sum\limits_{n = 1}^\infty  {\sum\limits_{m = 1}^{2^n  - 1} {\left( { - 1} \right)^{m + 1} } } 2^{ - n} f(a + m\left( {b - a} \right)2^{-n} )\\

\int_{-\pi}^{\pi} \sin(\alpha x) \sin^n(\beta x) dx = \textstyle{\left \{ \begin{array}{cc} (-1)^{(n+1)/2} (-1)^m \frac{2 \pi}{2^n} \binom{n}{m} & n \mbox{ odd},\ \alpha = \beta (2m-n) \\ 0 & \mbox{otherwise} \\ \end{array} \right .}\\

L = \int_a^b \sqrt{ \left|\sum_{i,j=1}^ng_{ij}(\gamma(t))\left(\frac{d}{dt}x^i\circ\gamma(t)\right)\left(\frac{d}{dt}x^j\circ\gamma(t)\right)\right|}\,dt\\

\begin{array}{rl} s &= \int_a^b\left\|\frac{d}{dt}\vec{r}\,(u(t),v(t))\right\|\,dt \\ &= \int_a^b \sqrt{u'(t)^2\,\vec{r}_u\cdot\vec{r}_u + 2u'(t)v'(t)\, \vec{r}_u\cdot\vec{r}_v+ v'(t)^2\,\vec{r}_v\cdot\vec{r}_v}\,\,\, dt. \end{array}\\

\end{array}$\end{document}  Command line: pdflatex -shell-escape New21.tex  This should generate a file New21.png containing the below picture: 3. Generation of the PNG file from Java, through a call to LaTeX Code: import java.awt.FlowLayout; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; class StreamPrinter implements Runnable { // Source: http://labs.excilys.com/2012/06/26/runtime-exec-pour-les-nuls-et-processbuilder/ private final InputStream inputStream; private boolean print; StreamPrinter(InputStream inputStream, boolean print) { this.inputStream = inputStream; this.print = print; } private BufferedReader getBufferedReader(InputStream is) { return new BufferedReader(new InputStreamReader(is)); } @Override public void run() { BufferedReader br = getBufferedReader(inputStream); String ligne = ""; try { while ((ligne = br.readLine()) != null) { if (print) { System.out.println(ligne); } } } catch (IOException e) { e.printStackTrace(); } } } public class Exemple141_LaTeX_to_PNG_using_installed_LaTeX_distribution { public static void main(String[] args) { String TEMP_DIRECTORY = "D:\\_tmp"; String TEMP_TEX_FILE_NAME = "New22"; // for New22.tex // 1. Prepare the .tex file String newLineWithSeparation = System.getProperty("line.separator")+System.getProperty("line.separator"); String math = ""; math += "\\documentclass[border=0.50001bp,convert={convertexe={imgconvert},outext=.png}]{standalone}" + newLineWithSeparation; math += "\\usepackage{amsfonts}" + newLineWithSeparation; math += "\\usepackage{amsmath}" + newLineWithSeparation; math += "\\begin{document}" + newLineWithSeparation; math += "$\\begin{array}{l}" + newLineWithSeparation;
math += "\\forall\\varepsilon\\in\\mathbb{R}_+^*\\ \\exists\\eta>0\\ |x-x_0|\\leq\\eta\\Longrightarrow|f(x)-f(x_0)|\\leq\\varepsilon\\\\" + newLineWithSeparation;
math += "\\det\\begin{bmatrix}a_{11}&a_{12}&\\cdots&a_{1n}\\\\a_{21}&\\ddots&&\\vdots\\\\\\vdots&&\\ddots&\\vdots\\\\a_{n1}&\\cdots&\\cdots&a_{nn}\\end{bmatrix}\\overset{\\mathrm{def}}{=}\\sum_{\\sigma\\in\\mathfrak{S}_n}\\varepsilon(\\sigma)\\prod_{k=1}^n a_{k\\sigma(k)}\\\\" + newLineWithSeparation;
math += "{\\sideset{_\\alpha^\\beta}{_\\gamma^\\delta}{\\mathop{\\begin{pmatrix}a&b\\\\c&d\\end{pmatrix}}}}\\\\" + newLineWithSeparation;
math += "\\int_0^\\infty{x^{2n} e^{-a x^2}\\,dx} = \\frac{2n-1}{2a} \\int_0^\\infty{x^{2(n-1)} e^{-a x^2}\\,dx} = \\frac{(2n-1)!!}{2^{n+1}} \\sqrt{\\frac{\\pi}{a^{2n+1}}}\\\\" + newLineWithSeparation;
math += "\\int_a^b{f(x)\\,dx} = (b - a) \\sum\\limits_{n = 1}^\\infty  {\\sum\\limits_{m = 1}^{2^n  - 1} {\\left( { - 1} \\right)^{m + 1} } } 2^{ - n} f(a + m\\left( {b - a} \\right)2^{-n} )\\\\" + newLineWithSeparation;
math += "\\int_{-\\pi}^{\\pi} \\sin(\\alpha x) \\sin^n(\\beta x) dx = \\textstyle{\\left \\{ \\begin{array}{cc} (-1)^{(n+1)/2} (-1)^m \\frac{2 \\pi}{2^n} \\binom{n}{m} & n \\mbox{ odd},\\ \\alpha = \\beta (2m-n) \\\\ 0 & \\mbox{otherwise} \\\\ \\end{array} \\right .}\\\\" + newLineWithSeparation;
math += "L = \\int_a^b \\sqrt{ \\left|\\sum_{i,j=1}^ng_{ij}(\\gamma(t))\\left(\\frac{d}{dt}x^i\\circ\\gamma(t)\\right)\\left(\\frac{d}{dt}x^j\\circ\\gamma(t)\\right)\\right|}\\,dt\\\\" + newLineWithSeparation;
math += "\\begin{array}{rl} s &= \\int_a^b\\left\\|\\frac{d}{dt}\\vec{r}\\,(u(t),v(t))\\right\\|\\,dt \\\\ &= \\int_a^b \\sqrt{u'(t)^2\\,\\vec{r}_u\\cdot\\vec{r}_u + 2u'(t)v'(t)\\, \\vec{r}_u\\cdot\\vec{r}_v+ v'(t)^2\\,\\vec{r}_v\\cdot\\vec{r}_v}\\,\\,\\, dt. \\end{array}\\\\" + newLineWithSeparation;
math += "\\end{array}\$" + newLineWithSeparation;
math += "\\end{document}";

// 2. Create the .tex file
FileWriter writer = null;
try {
writer = new FileWriter(TEMP_DIRECTORY + "\\" + TEMP_TEX_FILE_NAME + ".tex", false);
writer.write(math, 0, math.length());
writer.close();
} catch (IOException ex) {
ex.printStackTrace();
}

// 3. Execute LaTeX from command line  to generate picture
ProcessBuilder pb = new ProcessBuilder("pdflatex", "-shell-escape", TEMP_TEX_FILE_NAME + ".tex");
pb.directory(new File(TEMP_DIRECTORY));
try {
Process p = pb.start();
StreamPrinter fluxSortie = new StreamPrinter(p.getInputStream(), false);
StreamPrinter fluxErreur = new StreamPrinter(p.getErrorStream(), false);
p.waitFor();
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}

// 4. Display picture
JFrame maFrame = new JFrame();
maFrame.setResizable(false);
maFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
maFrame.setSize(400, 400);
maFrame.getContentPane().setLayout(new FlowLayout());
maFrame.getContentPane().add(new JLabel(new ImageIcon(TEMP_DIRECTORY + "\\" + TEMP_TEX_FILE_NAME + ".png")));
maFrame.pack();
maFrame.setVisible(true);

// 5. Delete files
for (File file : (new File(TEMP_DIRECTORY).listFiles())) {
if (file.getName().startsWith(TEMP_TEX_FILE_NAME + ".")) {
file.delete();
}
}
}
}


The above code should create the below frame:

Using Java's Runtime class you can execute external processes, so you can easily generate some LaTeX input file that contains the LaTeX expressions, execute pdflatex on the input file, possibly use Runrime to convert the pdf output to a different format (with gs) and open the resulting picture.

If you know Java this should be easy to implement. I don't have time to do this myself, but I'm confident this shouldn't take more than an hour or so.

I know very little about Java programming, but I am pretty sure that there are free applications that use JLaTeXMath to render LaTeX that you can try and look at. I have pretty good experience with Geogebra. The rendering in Geogebra is pretty fast (generally does not seem to slow the application down in any way), and the output is rather nice, I think. It is a free software, so you can download the source code and see how they do it.

Also look at DataMelt http://jwork.org/dmelt You can create PNG files from Latex equations using the Java class jhplot.HLatexEq

Have you seen JavaTeX? If that doesn't solve your problem, can you describe the features you're looking for and other constraints?

• This is a program, but I am looking for a Java library. I want to give a String, and receive anything I can draw on a JPanel or any other JComponent, preferably a BufferedImage. (This is why I asked this question on SO, they probably know a lot more about this kind of stuff.) – Hidde Jan 19 '12 at 16:40
• The source is available on sourceforge. You should be able to extract the functionality to a library. – recluze Jan 19 '12 at 16:42
• JavaTeX is NTS combined with a dvi viewer. That's nice, but also a 10 year old dead end. – Martin Schröder Jan 19 '12 at 21:57

SnuggleTeX is used by RTextDoc RTextDoc text editor to convert Latex to HTML