Java 应用程序中的 TeX 渲染

Java 应用程序中的 TeX 渲染

我想在我的 Java GUI 应用程序中渲染数学公式。我正在寻找一个专为计算机使用而非 Web 使用而制作的库,它可以渲染 TeX。我已经尝试过 jLatexMath,但在我的应用程序中使用它时速度非常慢。

如果还有其他/更好的,您能给我指明正确的方向吗?

答案1

信息 温馨提示:大多数转换过程涉及输入解析、评估、字体加载、渲染和输出,因此我不会期望近乎实时的转换。

话虽如此,我们开始做事吧。:)

信息 警告:前面是无聊的非 TeX 技术 Java 内容。:)

我快速浏览了一下SnuggleTeX欧几里得想出这个答案。抱歉,我没有时间想出更好的例子。

第一个,SnuggleTeX,被描述为“一个免费的开源 Java 库,用于将 LaTeX 片段转换为 XML(通常是 XHTML + MathML)。”第二个,欧几里得,被描述为“一个完整的 MathML 渲染解决方案”。我实际上所做的是将一个的输出重定向到另一个的输入。

首先,SnuggleTeX,你可以从最小示例在其自己的主页上:

/* 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 <math>...</math> element. */
String xmlString = session.buildXMLString();

现在,您有了 LaTeX 输入的 MathML 表示。让我们检查一下欧几里得,从其API,并且有具有Converter以下方法的类:

BufferedImage render(Node node, LayoutContext context) 

然后,您可以使用net.sourceforge.jeuclid.MathMLParserSupport将 XML 字符串解析为org.w3c.dom.Documentrender使用正确的参数调用该方法将为您提供一个BufferedImage表示输入的内容。

我的尝试:

我的代码

渲染该图像大约需要 1.4 秒。

我不喜欢这个输出,但说实话,我只是在 2 分钟内编写了这个应用程序作为[cough... cough... bad...]概念验证。:)我几乎可以肯定渲染质量可以提高,但我现在很忙。无论如何,我认为你可以尝试一些类似的东西,然后决定这种方法是否值得一试。:)

更新:它似乎欧几里得有一个JMathComponent以便在 中显示 MathML 内容Component

答案2

另一个解决方案是调用 mimetex.cgi(可从此处获取:http://www.forkosh.com/mimetex.html) 来自 Java。

我并不认为这个解决方案比之前给出的解决方案“更好”。目的只是为了提供替代方案。

结果示例:

在此处输入图片描述

导致此结果的代码:

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:
        int bytesRead;
        byte[] buffer = new byte[BUFFER_SIZE];
        ArrayList<byte[]> al = new ArrayList<byte[]>();
        while ((bytesRead = bis.read(buffer)) != -1) {
            al.add(buffer.clone());
        }
        // 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));
    jframe.add(button2, BorderLayout.CENTER);

    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);
}
}

尼古拉斯

答案3

从 LaTeX 创建 PNG 图像的另一种解决方案前提是计算机上安装了 LaTeX(例如:MiKTeX)...

LaTeX 独立包允许创建 PNG 输出文件,其大小与公式或文本的大小完全对应。

因此,我们只需从 Java 调用 LaTeX,并获取 PNG 输出文件。

1. 先决条件

a) 计算机上应安装 LaTeX
... 以及公式所需的所有软件包(在下面的示例中:amsfonts 和 amsmath)
... 以及独立软件包

必须安装 GhostScript(独立包必需)
必须将包含 gswin32c.exe 的目录添加到 PATH。
在我的计算机上:C:\Program Files (x86)\gs\gs9.10\bin

应安装 ImageMagick(独立包必需)
convert.exe 应重命名为 imgconvert.exe
应将包含 imgconvert.exe 的目录添加到 PATH。
在我的计算机上:C:\Program Files (x86)\ImageMagick-6.8.8-9

2. 检查 LaTeX(带有独立包)是否成功生成 PNG 文件(此阶段没有 Java)。

参考 :将 LaTeX 文档编译为尽可能短的 PNG 图像

LaTeX 文件,名为 New21.tex(例如):

\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}

命令行:

pdflatex -shell-escape New21.tex

这将生成一个包含下图的文件 New21.png: 在此处输入图片描述

3. 通过调用 LaTeX 从 Java 生成 PNG 文件

代码:

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);
            new Thread(fluxSortie).start();
            new Thread(fluxErreur).start();
            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();
            }
        }
    }
}

上述代码应创建以下框架: 在此处输入图片描述

答案4

KeenType是现代化的版本国家运输安全委员会它提供了一种从纯 TeX 文档生成 SVG 文档的方法。KeenType 取代了 KeenTeX,后者是数学数学项目。

用法:

final var str = "$\\sigma=\\sqrt{\\sum\\limits_{i=1}^{k} p_i(x_i-\\mu)^2}$";
final var typesetter = new KeenType();
final var svg = typesetter.toSvg( str );

在我的计算机上,KeenType 可以在不到 2 秒的时间内渲染 1,000 个简单方程式。

SVG 可以使用纯 Java SVG 光栅化器进行渲染BufferedImage,例如回声SVG. 以下是输出写作,我的基于 Java 的 Markdown 编辑器:

KeenType

相关内容