opencollab / jlatexmath

A Java API to render LaTeX
Other
514 stars 109 forks source link

Convert example: replace Apache Batik with JFreeSVG #69

Closed ghost closed 4 years ago

ghost commented 4 years ago

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).

ghost commented 4 years ago

Closing as what library to use for generating SVG should be to the users of JLaTeXMath to decide.