4

I use the batik-transcoder to read SVG files and transform them to BufferedImage. My problem is, that maven downloads about 19 another jars to compile my code. Which jars are required for my use case?

The list of jars which are downloaded by maven is:

[INFO] |  +- org.apache.xmlgraphics:batik-transcoder:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:fop:jar:0.94:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:xmlgraphics-commons:jar:1.2:compile
[INFO] |  |  |  +- org.apache.avalon.framework:avalon-framework-api:jar:4.3.1:compile
[INFO] |  |  |  \- org.apache.avalon.framework:avalon-framework-impl:jar:4.3.1:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-awt-util:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-bridge:jar:1.7:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:batik-anim:jar:1.7:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:batik-css:jar:1.7:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:batik-ext:jar:1.7:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:batik-parser:jar:1.7:compile
[INFO] |  |  |  +- org.apache.xmlgraphics:batik-script:jar:1.7:compile
[INFO] |  |  |  \- xalan:xalan:jar:2.6.0:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-dom:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-gvt:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-svg-dom:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-svggen:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-util:jar:1.7:compile
[INFO] |  |  +- org.apache.xmlgraphics:batik-xml:jar:1.7:compile
[INFO] |  |  \- xml-apis:xml-apis-ext:jar:1.3.04:compile

P.S. Suggestions about another SVG libs are also welcome. I've already seen the SVGSalamander. It allows what I want but it don't looks like a good designed and good supported lib (IMHO).

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48

1 Answers1

0

If using gradle, the minimal dependencies can be included using:

  implementation 'org.apache.xmlgraphics:batik-anim:1.13'
  implementation 'org.apache.xmlgraphics:batik-awt-util:1.13'
  implementation 'org.apache.xmlgraphics:batik-bridge:1.13'
  implementation 'org.apache.xmlgraphics:batik-css:1.13'
  implementation 'org.apache.xmlgraphics:batik-dom:1.13'
  implementation 'org.apache.xmlgraphics:batik-ext:1.13'
  implementation 'org.apache.xmlgraphics:batik-gvt:1.13'
  implementation 'org.apache.xmlgraphics:batik-parser:1.13'
  implementation 'org.apache.xmlgraphics:batik-script:1.13'
  implementation 'org.apache.xmlgraphics:batik-svg-dom:1.13'
  implementation 'org.apache.xmlgraphics:batik-svggen:1.13'
  implementation 'org.apache.xmlgraphics:batik-transcoder:1.13'
  implementation 'org.apache.xmlgraphics:batik-util:1.13'
  implementation 'org.apache.xmlgraphics:batik-xml:1.13'

JasperReports 5.0 suggests essentially the same collection when deploying reports to a server. The difference is that batik-transcoder (relatively new?) isn't on their list (though I imagine it is required for later versions of JasperReports):

batik-anim.jar
batik-awt-util.jar
batik-bridge.jar
batik-css.jar
batik-dom.jar
batik-ext.jar
batik-gvt.jar
batik-parser.jar
batik-script.jar
batik-svg-dom.jar
batik-svggen.jar
batik-util.jar
batik-xml.jar

To get everything:

  implementation 'org.apache.xmlgraphics:batik-all:1.13'

Change the version number as necessary.

Note: I've only tested this against two SVG illustrations. Both rendered flawlessly. There could be some dependencies that aren't required (such as batik-anim) depending on your requirements.


SVG Salamander is another SVG Engine. Lighter than Batik, it isn't as robust. Note that Batik cannot handle textual flowRoot elements easily and may result in black boxes. Eliminating the black boxes requires removing all flowRoot elements, which can take some time. (The other possibility is to perform an XSL transformation on the SVG file to fix the flowRoot issue, but that approach probably won't address all the issues encountered.)


Here's an example SVGRasterizer class that builds and works with the aforementioned subset of Batik library files:

public class SVGRasterizer {
  private final static SAXSVGDocumentFactory mFactory =
      new SAXSVGDocumentFactory( getXMLParserClassName() );

  private final static Map<Object, Object> RENDERING_HINTS = Map.of(
      KEY_ALPHA_INTERPOLATION,
      VALUE_ALPHA_INTERPOLATION_QUALITY,
      KEY_INTERPOLATION,
      VALUE_INTERPOLATION_BICUBIC,
      KEY_ANTIALIASING,
      VALUE_ANTIALIAS_ON,
      KEY_COLOR_RENDERING,
      VALUE_COLOR_RENDER_QUALITY,
      KEY_DITHERING,
      VALUE_DITHER_DISABLE,
      KEY_RENDERING,
      VALUE_RENDER_QUALITY,
      KEY_STROKE_CONTROL,
      VALUE_STROKE_PURE,
      KEY_FRACTIONALMETRICS,
      VALUE_FRACTIONALMETRICS_ON,
      KEY_TEXT_ANTIALIASING,
      VALUE_TEXT_ANTIALIAS_OFF
  );

  private static class BufferedImageTranscoder extends ImageTranscoder {
    private BufferedImage mImage;

    @Override
    public BufferedImage createImage( final int w, final int h ) {
      return new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB );
    }

    @Override
    public void writeImage(
        final BufferedImage image, final TranscoderOutput output ) {
      mImage = image;
    }

    public Image getImage() {
      return mImage;
    }

    @Override
    protected ImageRenderer createRenderer() {
      final ImageRenderer renderer = super.createRenderer();
      final RenderingHints hints = renderer.getRenderingHints();
      hints.putAll( RENDERING_HINTS );

      renderer.setRenderingHints( hints );

      return renderer;
    }
  }

  /**
   * Rasterizes the vector graphic file at the given URL. If any exception
   * happens, a red circle is returned instead.
   *
   * @param url   The URL to a vector graphic file, which must include the
   *              protocol scheme (such as file:// or https://).
   * @param width The number of pixels wide to render the image. The aspect
   *              ratio is maintained.
   * @return Either the rasterized image upon success or a red circle.
   */
  public static Image rasterize( final String url, final int width ) {
    try {
      return rasterize( new URL( url ), width );
    } catch( final Exception e ) {
      return createPlaceholderImage( width );
    }
  }

  /**
   * Converts an SVG drawing into a rasterized image that can be drawn on
   * a graphics context.
   *
   * @param url   The path to the image (can be web address).
   * @param width Scale the image width to this size (aspect ratio is
   *              maintained).
   * @return The vector graphic transcoded into a raster image format.
   * @throws IOException         Could not read the vector graphic.
   * @throws TranscoderException Could not convert the vector graphic to an
   *                             instance of {@link Image}.
   */
  public static Image rasterize( final URL url, final int width )
      throws IOException, TranscoderException {
    return rasterize(
        (SVGDocument) mFactory.createDocument( url.toString() ), width );
  }

  public static Image rasterize(
      final SVGDocument svg, final int width ) throws TranscoderException {
    final var transcoder = new BufferedImageTranscoder();
    final var input = new TranscoderInput( svg );

    transcoder.addTranscodingHint( KEY_BACKGROUND_COLOR, WHITE );
    transcoder.addTranscodingHint( KEY_WIDTH, (float) width );
    transcoder.transcode( input, null );

    return transcoder.getImage();
  }

  @SuppressWarnings("SuspiciousNameCombination")
  private static Image createPlaceholderImage( final int width ) {
    final var image = new BufferedImage( width, width, TYPE_INT_RGB );
    final var graphics = (Graphics2D) image.getGraphics();

    graphics.setColor( RED );
    graphics.setStroke( new BasicStroke( 5 ) );
    graphics.drawOval( 5, 5, width / 2, width / 2 );

    return image;
  }
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315