cbender
cbender

Reputation: 2251

How to display a svg byte array as an image in a JasperReport?

I have an image saved as a byte[] and I would like to display it as an image in a JasperReport. I have tried getting the data from Java methods:

public InputStream getImage(){
  return new ByteArrayInputStream(getImageByteArray());
}

and

public Image getImage() throws IOException{
    return ImageIO.read(new ByteArrayInputStream(getImageByteArray()));
}

and

public String getImage(){
  return new String((new org.apache.commons.codec.binary.Base64()).encode(getImageByteArray()));
}

but none of them seem to be working.

The jrxml looks like this:

<image hAlign="Center" vAlign="Middle" isUsingCache="true" isLazy="true">
   <reportElement positionType="Float" x="0" y="0" width="164" height="32" isRemoveLineWhenBlank="true" isPrintWhenDetailOverflows="true" uuid="c63c84a8-41c7-4ca3-8451-751d43fa8a9e"/>
   <imageExpression><![CDATA[$P{paramObject}.getImage()]]></imageExpression>
</image>

Some of things I try get exceptions and some print the JasperReport but the area where the image is supposed to be is blank. I know the image data is there because I can display it in a JSF page. The image data is SVG data.

Upvotes: 6

Views: 29149

Answers (7)

Mateusz
Mateusz

Reputation: 389

Injecting .svg image - clean code solution

byte[] svgPayload = .. // input array of svg file

Changes in code

Map<String, Object> parameters = new HashMap<>();
parameters.put("logoBytes", new ByteArrayInputStream(svgPayload));

Changes in Jasper Report template

<parameter name="logoBytes" class="java.io.ByteArrayInputStream"/>

...

<image>
   <imageExpression>
       <![CDATA[$P{logoBytes}]]>
   </imageExpression>
</image>

Upvotes: 0

whoisacat
whoisacat

Reputation: 11

getImage() should return byte[]

I can't see what type is paramObject in your jasperreports template, but it should be String

And the easiest way to deserialize pic without any unavailable oracle libraries is:

<imageExpression><![CDATA[javax.imageio.ImageIO.read(new java.io.ByteArrayInputStream(java.util.Base64.getDecoder().decode($F{paramObject})))]]></imageExpression>

Upvotes: 1

tamkhade mahesh
tamkhade mahesh

Reputation: 1

The definition of our field for the image must be of type Objet:

And then in the body of the report, we must place the image with the following expression:

this code is work for me....

Upvotes: -2

Dave Jarvis
Dave Jarvis

Reputation: 31161

Custom Image Transcoder

Write a custom image transcoder that can read an SVG file and transform that resource into a PNG or SVG file. When exporting as PDF, it is okay to use an SVG file directly. Consider:

import java.awt.Color;
import java.io.*;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.util.JRLoader;
import org.apache.batik.transcoder.*;
import static org.apache.batik.transcoder.image.ImageTranscoder.*;
import org.apache.batik.transcoder.image.PNGTranscoder;

public class ImageTranscoder {
    public static InputStream asSVG(final String file) throws JRException {
        return new ByteArrayInputStream(load(file));
    }

    public static InputStream asPNG(final String file)
            throws TranscoderException, JRException {
        return asPNG(load(file));
    }

    public static InputStream asPNG(final byte[] svg)
            throws TranscoderException {
        final ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
        final ByteArrayInputStream inBytes = new ByteArrayInputStream(svg);

        final TranscoderInput input = new TranscoderInput(inBytes);
        final TranscoderOutput output = new TranscoderOutput(outBytes);
        final PNGTranscoder transcoder = new PNGTranscoder();

        transcoder.addTranscodingHint(KEY_BACKGROUND_COLOR, Color.white);
        transcoder.addTranscodingHint(KEY_FORCE_TRANSPARENT_WHITE, true);
        transcoder.transcode(input, output);

        final byte[] bytes = outBytes.toByteArray();
        return new ByteArrayInputStream(bytes);
    }

    private static byte[] load(final String file) throws JRException {
        return JRLoader.loadBytesFromResource(file);
    }
}

Import Transcoder

In the JRXML file, import the fully qualified class:

<import value="com.company.jasper.ImageTranscoder"/>

Apply Image Transcoder

Drag and drop an Image from the palette onto the report, as per usual. Set its Expression to:

ImageTranscoder.asSVG($P{IMAGES_PATH} + $P{IMAGE_FILENAME} + ".svg")

If you absolutely need a PNG version, then transcode it on-the-fly:

ImageTranscoder.asPNG($P{IMAGES_PATH} + $P{IMAGE_FILENAME} + ".svg")

HTML vs PDF

With HTML, typically PNG images are still preferred. There are a number of approaches you can take to differentiate HTML (PNG) from PDF (SVG). A simple way is to assign two different key values to two different Image elements. For example:

<image scaleImage="RetainShape" onErrorType="Blank">
    <reportElement key="IMAGE_PNG"/>
    <imageExpression><![CDATA[ImageTranscoder.asPNG(...)]]></imageExpression>
</image>
<image scaleImage="RetainShape" onErrorType="Blank">
    <reportElement key="IMAGE_SVG"/>
    <imageExpression><![CDATA[ImageTranscoder.asSVG(...)]]></imageExpression>
</image>

Then you can exclude one or the other based on the export type:

<property name="net.sf.jasperreports.export.html.exclude.key.IMAGE_SVG"/>
<property name="net.sf.jasperreports.export.pdf.exclude.key.IMAGE_PNG"/>

Summary

While it is simpler to include a PNG image, converting that PNG image from SVG is an additional step that can be avoided. Since the JasperReports Library uses the Batik engine for rendering images, leverage it to convert an SVG file to PNG when the report is generated.

This way, the SVG serves as a single source for all formats, regardless of whether a PNG or SVG file is used in the report.

Be sure to set the IMAGES_PATH and IMAGE_FILENAME parameters as appropriate.

HTML and Base64

Force the image to embed using:

<property name="net.sf.jasperreports.export.html.embed.image" value="true"/>

The PNG image becomes a Base64-encoded String:

<img src="data:image/png;base64,..."/>

This will make the report load a bit faster (no extra HTTP request for the image) and simplify the architecture as it eliminates an external dependency. That is, a web server is no longer required to serve the image, since it is wholly embedded.

Upvotes: 7

Aram Arabyan
Aram Arabyan

Reputation: 2359

Assuming that you have image bytes encoded base64, you can use following

<image>
    <reportElement/>
    <imageExpression>
        <![CDATA[javax.imageio.ImageIO.read(new java.io.ByteArrayInputStream(new sun.misc.BASE64Decoder().decodeBuffer("/9j/4AAQ .... "))) ]]>
    </imageExpression>
</image>

Upvotes: 5

cesAR
cesAR

Reputation: 702

I propose a simple solution. It works, I'm using it. This way we avoid intermediate code and modifications to our application.

We must establish the following Import:

<import value="javax.swing.ImageIcon"/>

The definition of our field for the image must be of type Objet:

<field name="myImage" class="java.lang.Object"/>

And then in the body of the report, we must place the image with the following expression:

<image>
  <reportElement x="10" y="10" width="100" height="100" uuid="a791129e-a20d-4be3-bdcd-27528bf2edc4"/>
  <imageExpression><![CDATA[(new ImageIcon((byte[])$F{foto})).getImage()]]></imageExpression>
</image>

That is all. How does the solution work? We import the javax.swing.ImageIcon library, which allows us to create a new ImageIcon from a byte[], then we can convert an ImageIcon to Image using getImage().

Upvotes: 1

Madushan Perera
Madushan Perera

Reputation: 2598

You can try this :

Set your image parameter (here it is img) to InputStream

<parameter name="img" class="java.io.InputStream">
        <parameterDescription><![CDATA[]]></parameterDescription>
    </parameter>

Then set onErrorType="Blank" to your image element(Actually dont know the reason to this :) )

<image onErrorType="Blank">
    <reportElement x="2" y="4" width="119" height="62" uuid="66857471-6aa2-4ff0-be59-e2e1b0214bfc"/>
    <imageExpression><![CDATA[$P{img}]]></imageExpression>
</image>

Upvotes: 2

Related Questions