opencollab / jlatexmath Goto Github PK
View Code? Open in Web Editor NEWA Java API to render LaTeX
License: Other
A Java API to render LaTeX
License: Other
Right now, the parser is slow, bad-designed. So the goal is to have something very fast but it requires to rewrite parser itself and rewrite almost all the macros which take some strings as arguments rather than atom.
We use the mhchem (http://docs.mathjax.org/en/latest/tex.html#mhchem ) package for chemistry when using Latex/MathJax. It lets us easily format chemical formulas and reactions without typing too much.
There really is only one command you need to know here: \ce{...}. \ce{...} takes its parameters and automatically formats it. For example,
Would it be possible to add mchem to JLatexMath and would it be a big effort to add it to JLatexMath?
Thanks for an answer.
°C
gets changed to
\\circC
Suggested fix:
parseString.replace(pos, pos + 1, "^\\circ");
needs to be changed to this in TexParser.java
parseString.replace(pos, pos + 1, "^{\\circ}");
Test-case:
"\rotatebox{90.0}{ \text{ Temperature [°C] } }"
According to version.xml the current version is 1.0.5 - it would be nice if github releases reflected that and a offered a jar file for direct download.
for example, when I need
Easy change if it's wanted 😄
http://dev.geogebra.org/trac/changeset/51465
eg
123 \longdiv{456}
eg
"{\begin{array}{|l|l|}\hline \cellcolor{#FFEFD5}{1}&\cellcolor{#FF0000}{3} \\ \hline \cellcolor{#0000FF}{2}&\cellcolor{#BFFF00}{4} \\ \hline \end{array}}"
Added in our fork here:
https://dev.geogebra.org/trac/changeset/48883
https://dev.geogebra.org/trac/changeset/48817
https://dev.geogebra.org/trac/changeset/48816
https://dev.geogebra.org/trac/changeset/48813
https://dev.geogebra.org/trac/changeset/48812
https://dev.geogebra.org/trac/changeset/48810
Clearer per-file changes:
https://dev.geogebra.org/trac/changeset/48810/trunk/geogebra/retex/renderer-base/src/main/java/com/himamis/retex/renderer/share/PredefinedCommands.java
https://dev.geogebra.org/trac/changeset?reponame=&new=48810%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FMultlineAtom.java&old=45809%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FMultlineAtom.java
https://dev.geogebra.org/trac/changeset?reponame=&new=48882%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FMatrixAtom.java&old=48810%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FMatrixAtom.java
https://dev.geogebra.org/trac/changeset?reponame=&new=49178%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FPredefMacros.java&old=48810%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FPredefMacros.java
https://dev.geogebra.org/trac/changeset?reponame=&new=48812%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FTableBox.java&old=48447%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FTableBox.java
https://dev.geogebra.org/trac/changeset?reponame=&new=48812%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FArrayOfAtoms.java&old=45809%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FArrayOfAtoms.java
https://dev.geogebra.org/trac/changeset?reponame=&new=48810%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FTeXParser.java&old=48111%40trunk%2Fgeogebra%2Fretex%2Frenderer-base%2Fsrc%2Fmain%2Fjava%2Fcom%2Fhimamis%2Fretex%2Frenderer%2Fshare%2FTeXParser.java
For some reason fonts in SVG output I'm getting from JLaTeXMath are just a bit misshapen.
Example:
A = \frac{2394}{b \cdot 1650}
PNG:
SVG:
B771A71686B3F75C52A0298E8288BF80A6738EA9A433A51140F9514884F50304.zip
Code I use:
private final static String SVG_NS = "http://www.w3.org/2000/svg";
private final static String SVG_ROOT = "svg";
private final static String SVG_EXT = ".svg";
private final static float FONT_SIZE = 12;
private String renderLatex(String source) {
DOMImplementation DOMImpl = GenericDOMImplementation.getDOMImplementation();
Document document = DOMImpl.createDocument(SVG_NS, SVG_ROOT, null);
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document);
SVGGraphics2D g = new SVGGraphics2D(ctx, true);
TeXFormula formula = new TeXFormula(source);
TeXFormula.TeXIconBuilder builder = formula.new TeXIconBuilder();
builder.setStyle(TeXConstants.STYLE_DISPLAY);
builder.setSize(FONT_SIZE);
TeXIcon icon = builder.build();
icon.setInsets(new Insets(0, 0, 0, 0));
g.setSVGCanvasSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
g.setColor(Color.WHITE);
g.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight());
icon.paintIcon(null, g, 0, 0);
StringWriter out = new StringWriter();
try {
g.stream(out, true);
out.flush();
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return out.toString();
}
Package versions:
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-dom</artifactId>
<version>1.8</version>
<scope>provided</scope>
</dependency>
Hopefully quite easy to backport from our fork:
http://dev.geogebra.org/trac/changeset/51878
env: jvm 1.8.0_45 jlatexmath 1.0.6
input formula string :a/leq b output image sometimes like this
or like this
.
after debug, i find the different, "leq" mapping to a symbol instance ,it's type sometimes 0 and sometimes 3.
in fact , "leq" symbol instance's type value should be 3, may be there is something wrong when init?
Examples
\begin{tabular}{lccccc c ccccc}
\hline
& \multicolumn{5}{c}{Females} && \multicolumn{5}{c}{Males} \\
\cline{2-6} \cline{8-12}
Treatment & V1 & V2 & V3 & V4 & V5 && V1 & V2 & V3 & V4 & V5 \\
Placebo & 0.21 & 163 & 3 & 4 & 5 && 0.22 & 164 & 3 & 4 & 5 \\
ACE Inhibitor & 0.13 & 142 & 3 & 4 & 5 && 0.15 & 144 & 3 & 4 & 5 \\
Hydralazine & 0.17 & 143 & 3 & 4 & 5 && 0.16 & 140 & 3 & 4 & 5 \\
\end{tabular}
\begin{tabular}{|r|l|}
\hline
7C0 & hexadecimal \\
3700 & octal \\ \cline{2-2}
11111000000 & binary \\
\hline \hline
1984 & decimal \\
\hline
\end{tabular}
The last version published on the maven central repository is 1.0.2. It would be much appreciated if you could publish the latest version.
I'm aware of the previous discussion of this topic. If you need help with it, I'm ready to help.
Another small suggestion:
NewCommandMacro.addNewCommand("questeq", "{\\stackrel{?}{=}}", 0);
From: https://forge.scilab.org/index.php/p/jlatexmath/issues/1571/
Not sure what causes the bug exactly, but changing \qquad to a proper command fixes it, eg
http://dev.geogebra.org/trac/changeset/49606
This article describes how it is possible in LaTeX to render a "closed square root".
I would like to draw one myself, but the code provided does not work. I suppose I am doing something wrong, because I literally started using this library today.
This is the LaTeX code provided by the article
% New definition of square root:
% it renames \sqrt as \oldsqrt
\let\oldsqrt\sqrt
% it defines the new \sqrt in terms of the old one
\def\sqrt{\mathpalette\DHLhksqrt}
\def\DHLhksqrt#1#2{%
\setbox0=\hbox{$#1\oldsqrt{#2\,}$}\dimen0=\ht0
\advance\dimen0-0.2\ht0
\setbox2=\hbox{\vrule height\ht0 depth -\dimen0}%
{\box0\lower0.4pt\box2}}
This is the result from the article. I am thinking I could just do something like...
TeXFormula fx = new TeXFormula("% New definition of square root:\r\n" +
"% it renames \\sqrt as \\oldsqrt\r\n" +
"\\let\\oldsqrt\\sqrt\r\n" +
"% it defines the new \\sqrt in terms of the old one\r\n" +
"\\def\\sqrt{\\mathpalette\\DHLhksqrt}\r\n" +
"\\def\\DHLhksqrt#1#2{%\r\n" +
"\\setbox0=\\hbox{$#1\\oldsqrt{#2\\,}$}\\dimen0=\\ht0\r\n" +
"\\advance\\dimen0-0.2\\ht0\r\n" +
"\\setbox2=\\hbox{\\vrule height\\ht0 depth -\\dimen0}%\r\n" +
"{\\box0\\lower0.4pt\\box2}}");
...but it doesn't work. Please help me.
I installed jlatexmath using mvn clean install but the lines
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGeneratorContext;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.fop.render.ps.EPSTranscoder;
import org.apache.fop.render.ps.PSTranscoder;
import org.apache.fop.svg.AbstractFOPTranscoder;
import org.apache.fop.svg.PDFTranscoder;
are still red.
P.S: these lines are ok
import org.scilab.forge.jlatexmath.DefaultTeXFont;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;
import org.scilab.forge.jlatexmath.cyrillic.CyrillicRegistration;
import org.scilab.forge.jlatexmath.greek.GreekRegistration;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
The glyph in jlm_special.ttf (for "m") has got messed up at some point and replaced with the Euro symbol so \textmu doesn't work
Here's a fixed version (needs replacing in master and experimental)
http://dev.geogebra.org/trac/export/68678/trunk/geogebra/retex/renderer-desktop/src/main/resources/com/himamis/retex/renderer/desktop/fonts/maths/jlm_special.ttf
fun getAlignment(i: Int): TeXConstants.Align {
return options[i + 1].alignment
}
There is a problem with this function
public ArrayOptions complete(final int n) {
final int s = options.size();
for (int i = 0; i < n - s; ++i) {
addAlignment(TeXConstants.Align.CENTER);
}
return this;
}
Because its options.size == n+1 ,Should be like this for (int i = 0; i <= n - s; ++i)
Test case
\begin{array}{cc}{-6-10} & {6-0} & {0+10} \\{9-35}& {3-5} & {12-30}\end{array}
\longleftarrow has a gap/notch in when drawn large:
Definition:
TeXFormula.predefinedTeXFormulasAsString.put("longleftarrow", "\\leftarrow\\joinrel\\relbar");
\joinrel has a constant of -2.6f
public static final Atom joinrel_macro() throws ParseException {
return new TypedAtom(TeXConstants.TYPE_RELATION, TeXConstants.TYPE_RELATION, new SpaceAtom(
TeXConstants.UNIT_MU, -2.6f, 0, 0));
}
changing this to around -3.4f seems to fix this case, which matches the value in XLeftRightArrowFactory but I don't know if this will break other things
Box kern = new SpaceAtom(TeXConstants.UNIT_MU, -3.4f, 0, 0).createBox(env);
Latex code
\[||f||_{L^\infty(\mathbb{R}^N)}^{1-\alpha} ||f||^{\alpha}_{L_1(\mathbb{R}^N)}\]
in Freeplane will generate output as in the picture.
If there is only a subscript, the length is computed from the subscript correctly. Same with supersript.
But if both are used the spacing is NOT computed from the longer one but only from the length of the superscript resulting in a mess as seen in the picture.
This may be an issue of Freeplane (in which case I'm sorry) but I don't have the time to pinpoint it and to me it seems most likely that the issue is caused by this package.
EDIT: In GeoGebra it works just fine so the issue is probably on the side of Freeplane. I am sorry to bother.
PLEASE CLOSE THIS ISSUE
Does jlatexmath's license permit its distribution with EPL licensed plantuml eclipse plugin? Please
see hallvard/plantuml#72 (comment)
It has been built without --offline
, repository has been saved and restored to build with --offline
but it again tries to download something:
[ERROR] Error fetching link: /usr/ports/math/jlatexmath/work/jlatexmath-1.0.7/jlatexmath-font-cyrillic/target/apidocs/package-list. Ignored it.
[ERROR] Error fetching link: /usr/ports/math/jlatexmath/work/jlatexmath-1.0.7/jlatexmath-font-greek/target/apidocs/package-list. Ignored it.
AFAIK, it has been removed with the ant migration.
Needs checking/fixing in 2 places
TeXParser.getArgAsPositiveInteger() returns -1
for the {}
argument
In LongDivAtom.makeResults() this could be improved :)
while (q != 0) {
A nice collection of (mostly) real world test cases:
latexTests2.txt
Some of them use \cellcolor{}, see #25
Just spotted this fix in a fork:
CBN-Polska@271489b
- boxarr[i][j - 1].type = TeXConstants.TYPE_INTERTEXT;
+ if (boxarr[i][j - 1] != nullBox) // BUGFIX: EN-122
+ boxarr[i][j - 1].type = TeXConstants.TYPE_INTERTEXT;
BUGFIX EN-122 solves problem:
When a matrix like this
\begin{tabular}{ccc}
& \\
& 1 & 2 \\
\end{tabular}
is created then all next matrixes having first cell in a row empty have all row empty.
So when you call above tex and later write for example:
\begin{tabular}{ccc}
& 2 & 3 \\
4 & 5 & 6 \\
& 8 & 9 \\
\end{tabular}
you will see only 4 5 6. First and third line are print empty.
This looks odd, and I can't see a reason for it:
public int hashCode() {
return (left + right) % 128;
}
I suggest:
public int hashCode() {
return left + (right << 16);
}
What are your thoughts on having separate modules (or packages) that allow developers to easily pick and choose what TeX flavours they'd like to bundle with their applications?
It would require changes to the project's organizational structure. See my fork of JMathTex for details, most relevant would be the resource readers and resource hierarchy. Broadly, consider:
Under src/main/java/
:
Under src/main/resources/
:
Under src/main/resources/fonts/
:
hello jlatexmath developers,
many thanks for this excellent library!
I have a small problem in 1.0.6 when rendering a formula "тся" in Freeplane
for the first time (on Freeplane startup):
WARNING: Problem with command text at position 4:10
fonts/language_cyrillic.xml
org.scilab.forge.jlatexmath.ParseException: Problem with command text at position 4:10
fonts/language_cyrillic.xml
at org.scilab.forge.jlatexmath.PredefMacroInfo.invokeID(PredefMacroInfo.java:593)
at org.scilab.forge.jlatexmath.PredefMacroInfo.invoke(PredefMacroInfo.java:70)
at org.scilab.forge.jlatexmath.TeXParser.processCommands(TeXParser.java:1398)
at org.scilab.forge.jlatexmath.TeXParser.processEscape(TeXParser.java:1238)
at org.scilab.forge.jlatexmath.TeXParser.parse(TeXParser.java:712)
at org.scilab.forge.jlatexmath.TeXFormula.<init>(TeXFormula.java:284)
at org.scilab.forge.jlatexmath.TeXFormula.<init>(TeXFormula.java:268)
at org.freeplane.plugin.latex.TeXText.createTeXIcon(TeXText.java:39)
at org.freeplane.plugin.latex.LatexRenderer.getIcon(LatexRenderer.java:90)
Please tell me if you cannot reproduce this easily. In that case I will (try to) provide a minimal example.
Thanks and Best Regards,
Felix
Regarding the Convert example code, a quick performance comparison between Apache Batik and JFreeSVG revealed that Batik introduces a lot of garbage collection churn. (I did not tweak the GC parameters.) There are some benchmarks that show JFreeSVG can be up to five times faster than Batik when generating SVG representations.
Here's some example code for JFreeSVG:
// JFreeSVG uses the default buffer size, which otherwise incurs reallocations.
final StringBuilder buffer = new StringBuilder( 16384 );
final var formula = new TeXFormula( EQUATIONS[ j ] );
final var font = new DefaultTeXFont( size );
final var env = new TeXEnvironment( STYLE_DISPLAY, font );
final var box = formula.createBox( env );
final var layout = new TeXLayout( box, size );
final var g = new SVGGraphics2D(
layout.getWidth(), layout.getHeight(), PX, buffer );
g.setRenderingHint(
KEY_DRAW_STRING_TYPE, VALUE_DRAW_STRING_TYPE_VECTOR
);
g.scale( size, size );
box.draw( g, layout.getX(), layout.getY() );
svg = g.getSVGElement( null, true, null, null, null );
final String svg = g.getSVGElement( null, true, null, null, null );
try( final var fos = new FileOutputStream( filename );
final var out = new OutputStreamWriter( fos, UTF_8 ) ) {
out.write( svg );
}
This is, of course, a super minor issue. Here's the TeXLayout
class that's equivalent to TeXIcon
, but with some coupling to the rendering environment removed.
package be.ugent.caagt.jmathtex;
import java.awt.*;
/**
* Responsible for computing the preferred layout dimensions for an instance
* of {@link Box}. This provides similar functionality to {@link TeXIcon},
* but without {@link Insets} or painting ability. It is meant to help with
* calculating final layout dimensions that can be used to draw the
* {@link Box} using third-party libraries.
* <p>
* The dimensions returned for the width and height are in pixels, by default.
* </p>
*/
public class TeXLayout {
private static final float ROUND = 0.99f;
/**
* The generated {@link Box} width is a little off for some reason (possibly
* italics?), so this enlarges the resulting dimension a little.
*/
private static final float TWEAK_WIDTH = 0.16f;
/**
* The generated {@link Box} height is a little off for some reason (possibly
* italics?), so this enlarges the resulting dimension a little.
*/
private static final float TWEAK_HEIGHT = 0.18f;
private final Box mBox;
private final float mSize;
public TeXLayout( final Box box, final float size ) {
mBox = box;
mSize = size;
}
/**
* Returns the suggested X position for to (mostly) center-align the result.
*
* @return Recommended X location for drawing the {@link Box}.
*/
public float getX() {
return TWEAK_WIDTH / 2;
}
/**
* Returns the suggested Y position for to (mostly) center-align the result.
*
* @return Recommended Y location for drawing the {@link Box}.
*/
public float getY() {
return getBox().getHeight() + TWEAK_HEIGHT / 2;
}
/**
* Get the total occupied space to render the {@link Box} width.
*
* @return The {@link Box} width multiplied by the size.
*/
public int getWidth() {
return (int) (getBox().getWidth() * getSize() + ROUND + (TWEAK_WIDTH * getSize()));
}
/**
* Get the total occupied space to render the {@link Box} height.
*
* @return The {@link Box} height and depth multiplied by the size.
*/
public int getHeight() {
final var box = getBox();
final var size = getSize();
return (int) ((box.getHeight() + box.getDepth()) * size + (2 * ROUND) + (TWEAK_HEIGHT * size));
}
private Box getBox() {
return mBox;
}
public float getSize() {
return mSize;
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
"mBox=" + mBox +
", mSize=" + mSize +
", width=" + getWidth() +
", height=" + getHeight() +
'}';
}
}
Centering the output within the SVG is proving problematic, but the above code ensures that all the content fits (in the several test cases I tried).
These two examples work fine:
"\begin{array}{|l|l|l||l|} \hline Frequency&a&a&Total \\ \hline a&1&1&2 \\ \hline a&1&1&2 \\ \hline Total&2&2&4 \\ \hline \end{array}"
"\begin{array}{r|lllll}0&1&1&1&1 \\ 1&2&2& & \\ 2& & & & \\ 3&3&5& & \\ 4&2& & & \\ 5& & & & \\ 6&3&3&6& \\ \end{array} "
but if you add a spurious \\ in the first command then the second one draws only the first row:
"\begin{array}{|l|l|l||l|} \\ \hline Frequency&a&a&Total \\ \hline a&1&1&2 \\ \hline a&1&1&2 \\ \hline Total&2&2&4 \\ \hline \end{array}"
"\begin{array}{r|lllll}0&1&1&1&1 \\ 1&2&2& & \\ 2& & & & \\ 3&3&5& & \\ 4&2& & & \\ 5& & & & \\ 6&3&3&6& \\ \end{array} "
Hey, my problem is that when we put a lot of text with some formulas into the library, I have to use \text{} again and again. Is there a way to set the parsing environment to plain text when parsing, instead of math mode, and use math mode only for
Very nice work! I am using this to print formulas and it works very well.
I need some help. I'm in need of retaining the variable names in the exported svg image.
When using the constructor of SVGGraphics2D there is an option to use font instead of shapes in the resulting svg image. But using font instead of shapes, does not paint the mathematical formula nicely.
SVGGraphics2D g2 = new SVGGraphics2D(ctx, fontAsShapes);
I require the variables names for post processing the svg image to embed hyperlinks. Thanks!
This is the result I get passing fontAsShape as false
This is the result I get passing fontAsShape as true
So in the above formula is it possible to group the entire variable End_fD in a single svg text group?
\hfil,\hfilll is not supported, how to replace it?
Does jlatexmath support following Elements? I am getting exception while processing them:
equation
org.scilab.forge.jlatexmath.ParseException: Unknown environment: equation at position 1:2
eqnarray*, align* (eqnarray and align are working without *)
org.scilab.forge.jlatexmath.ParseException: Unknown environment: eqnarray* at position 1:2
\nonumber
org.scilab.forge.jlatexmath.ParseException: Problem with command [ at position 2:343 Problem with command array@@env at position 0:311 Unknown symbol or command or predefined TeXFormula: 'nonumber'
I am generating PDF using Apache fop, jlatexmath and jlatexmath-fop.
Thanks
Hi
@calixteman asked that I proceed with the refactor to use maven as a build tool as discussed in #6.
I have a fork at https://github.com/davidmoten/jlatexmath that is looking good and I'll submit it as a PR soon. As preparatory work with @calixteman's permission I've removed the recent commit d7d9456 (Massive reindent) to simplify the work on the PR. The state of the master branch before the commit removal is at the branch https://github.com/opencollab/jlatexmath/tree/before-massive-indent-removal.
I'll submit the maven rework as a PR so that people have some visibility and chance to comment. Merge will be pretty soon after I imagine as we can then deal with any minor issues on master than on the PR.
Hello,
thanks a lot for this library.
A question here. Is there a way to group equations into SVG groups? I am trying to render one line at a time and it will be easy to achieve if there are distinct groups.
Guided by async profiler, I've made some improvements to my fork of JMathTeX that may be relevant. JMH notwithstanding it appears that the time to parse TeX has been halved. The output documents are not perfectly identical, but close enough to show the performance gains---see below for one hot spot.
Once @murkle's changes are on the mainline branch, we'll see how much performance is gained from the updates.
This section describes the setup for the two code bases.
The following expressions were tested:
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",
"\\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)",
};
Two loops, one through the equations and the other to parse 10,000,000 plain TeX strings.
final var size = 20f;
final var texFont = new DefaultTeXFont( size );
final long startTime = System.currentTimeMillis();
for( int j = 0; j < EQUATIONS.length; j++ ) {
// Unused, but doesn't affect the outcome.
final var filename = "/tmp/eq-" + j + ".svg";
for( int i = 0; i < 10_000_000 / EQUATIONS.length; i++ ) {
final var formula = new TeXFormula( EQUATIONS[ j ] );
final var env = new TeXEnvironment( STYLE_DISPLAY, texFont );
final var box = formula.createBox( env );
final var layout = new TeXLayout( box, size );
}
}
System.out.println( System.currentTimeMillis() - startTime );
First, strip down Convert.toSVG
to its TeX parsing parts:
public static void toSVG(String latex, String file, boolean fontAsShapes) throws IOException {
final TeXFormula formula = new TeXFormula(latex);
final TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_DISPLAY, 20);
icon.setInsets(new Insets(5, 5, 5, 5));
final Dimension dim = new Dimension(icon.getIconWidth(), icon.getIconHeight());
}
Then change Example5
to:
public static void main( String[] args ) throws IOException {
final long startTime = System.currentTimeMillis();
for( int j = 0; j < EQUATIONS.length; j++ ) {
for( int i = 0; i < 10_000_000 / EQUATIONS.length; i++ ) {
Convert.toSVG( EQUATIONS[ j ], "/tmp/example-" + j + ".svg", true );
}
}
System.out.println( System.currentTimeMillis() - startTime );
}
Results with 1 run each (no best of times):
Three runs of 10,000 iterations after further micro-optimizations to JMathTeX fork:
This gets the code within poking distance of real-time rendering for documents of significant size.
One of the larger performance gains was to eliminate using exceptions for conditional logic in TeXParser
's processEscape()
method. The following code is slow because Java will fill out an entire stack trace when a formula or a symbol cannot be found:
try {
return TeXFormula.get(command).root;
} catch (FormulaNotFoundException e) {
try {
return SymbolAtom.get(command);
} catch (SymbolNotFoundException e1) { }
}
Instead, consider using Optional
or return null
instead. The processEscape()
method is in a critical part of the code.
There is a rendering issue that occurs with JRE11, but not with JRE8 (or 1.7).
It happens when doing vector rendering, e.g. using any of the following Graphics2d backends: epsgraphics, freehep, jfreesvg, orsonpdf, jtikz. But none of this backends is affected when using g.drawString() instead of jlatexmath.
png output (ok for both JRE8 and JRE11):
JRE 11 (distorted):
out2_JRE11_eps.pdf
JRE 8 (ok):
out2_JRE8_eps.pdf
Unfortunately I was not able to track down the issue.
Example code (showIssue2.zip):
/* Test case */
package ShowIssue;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.io.FileOutputStream;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import net.sf.epsgraphics.ColorMode;
import net.sf.epsgraphics.EpsGraphics;
import org.scilab.forge.jlatexmath.Box;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;
/**
*
* @author av 2019
*/
public class showIssue2 {
boolean success = true;
public showIssue2 () {
int pxwidth = 80;
int pxheight = 50;
// writing to an image works in both JRE8 and JRE11
writeOutput(pxwidth, pxheight, "out2.png");
// writing to anything else distorts the height of jlatexmath formula
// tested: epsgraphics, jfreesvg, orsonpdf, jtikz, freehep
writeOutputEPS(pxwidth, pxheight, "out2.eps");
}
private void draw(Graphics2D g) {
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
int fontsize = 12;
g.setFont(new java.awt.Font("Serif", Font.PLAIN, fontsize));
g.drawString("x = √25", 10f, 15f);
double xpix = 10;
double ypix = 40;
String latex = "x = \\sqrt{25}";
TeXFormula formula = new TeXFormula(latex);
formula.setColor(g.getColor());
formula.setDEBUG(false);
//formula.createPNG(TeXConstants.STYLE_TEXT, 200, "out.png", Color.WHITE, Color.BLACK);
TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_TEXT, fontsize, true);
Graphics2D tempGraphics = (Graphics2D) g.create();
tempGraphics.translate(xpix, ypix);
tempGraphics.scale(fontsize, fontsize);
// draw formula box
Box box = icon.getBox();
box.draw(tempGraphics, 0, 0);
tempGraphics.dispose();
}
public static void main(String[] args) {
showIssue2 c1 = new showIssue2();
}
private void writeOutputEPS(int pxwidth, int pxheight, String filename) {
try {
FileOutputStream file = new FileOutputStream(filename);
EpsGraphics eps = new EpsGraphics(filename, file,
0, 0, pxwidth, pxheight, ColorMode.COLOR_RGB);
draw(eps);
eps.close();
file.close();
} catch (IOException ex) {
System.err.println(ex.getLocalizedMessage());
success = false;
}
if (success) {
System.out.println("Output written to: " + filename);
} else {
System.out.println("Error occurred. Check for error messages.");
}
}
private void writeOutput(int pxwidth, int pxheight, String filename) {
try {
FileOutputStream file = new FileOutputStream(filename);
int imgtype = BufferedImage.TYPE_INT_ARGB; // including transparency (alpha channel)
BufferedImage bi = new BufferedImage(pxwidth, pxheight, imgtype);
Graphics2D ig2 = bi.createGraphics();
ig2.setColor(Color.WHITE);
ig2.fillRect(0, 0, pxwidth, pxheight);
draw(ig2);
ImageIO.write(bi, "png", file);
file.close();
} catch (IOException ex) {
System.err.println(ex.getLocalizedMessage());
success = false;
}
if (success) {
System.out.println("Output written to: " + filename);
} else {
System.out.println("Error occurred. Check for error messages.");
}
}
}
Should be easy to backport from our fork, simple adaption of code for choose_macro() for {a \choose b}
http://dev.geogebra.org/trac/changeset/53154
eg
"{a \brace b}"
"{a \bangle b}"
"{a \brack b}"
There's a problem with the first bracket missing from both these which I didn't manage to solve though:
"{a \bangle b}"
"a \atopwithdelims \langle \rangle b"
I guess it's a font/glyph problem as it's OK in bold (but not italic)
Well, This is Awesome! Can this library generate Physical and Chemical formula?
hi, i use experimental branch.
i found \intop is incorrect when over script is null, for example \intop_1.
after \intop_1 is called, \intop is wrong too
Hello! I am maintaining another JMathTeX satellite project, WPF-Math, and I want to ask you a question about the legacy XML font description JMathTeX is using.
I can see that you're using and successfully modifying the same XML font format, and these XML files are obviously linked to the corresponding TTF files.
I'm curious: how are you extracting these XML files from the newly added fonts? How are you modifying these files? Are there any tools or scripts for that?
I've tried to ask JMathTeX author, Kris Coolsaet, but unfortunately it was so long ago that he didn't remember how these XML files were managed back then.
\left (\number{5000}-\text{_____}\right )\div 9=\number{555}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.