JasperReports is an extremely flexible tool for creating reports in Java. Reports are defined as JRXML templates, compiled to JASPER binaries and run through the JasperReports run-time. iReport is the GUI report writing tool that creates the JRXML templates.

A common requirement when displaying values is to highlight them by displaying them with a background color. But many background colors can cause values to be difficult to read or can make values illegible when reports are faxed or copied in black-and-white.

To get around this, a circular gradient background can be used. The resulting background looks like this:

gradient background

iReport lets you place images on a report that can come from a physical file or can be dynamically generated. In our case, we’ll write a simple Java class that will generate the gradient background.

A JRXML template is an XML file with a jrxml extension. Here’s an example of a very small template with a single parameter:

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
              name="comments" pageWidth="554" pageHeight="802" columnWidth="554" leftMargin="0" rightMargin="0"
              topMargin="0" bottomMargin="0">
<parameter name="displayValue" class="java.lang.String" isForPrompting="false"/>
<pageHeader>
        <band height="120">
            <image>
                <reportElement x="339" y="44" width="65" height="12">
                </reportElement>
                <imageExpression class="net.sf.jasperreports.engine.JRRenderable">
                    <![CDATA[new com.report.jasper.CircularGradientImageRenderer("#00FF00")]]></imageExpression>
            </image>
            <textField isBlankWhenNull="true">
                <reportElement x="339" y="44" width="65" height="12"/>
                <textElement textAlignment="Center" verticalAlignment="Middle">
                    <font fontName="DejaVu Sans" size="7"/>
                </textElement>
                <textFieldExpression class="java.lang.String"><![CDATA[$P{displayValue}]]></textFieldExpression>
            </textField>
        </band>
    </pageHeader>
</jasperReport>

Here’s a Java class that will generate a gradient background. Note that the example provided here will only work when generating PDF reports.

package com.report.jasper;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import net.sf.jasperreports.engine.JRAbstractSvgRenderer;
import net.sf.jasperreports.engine.JRException;

/**
 * Draws a colored, circular gradient background used in the report.
 */
public class CircularGradientImageRenderer extends JRAbstractSvgRenderer {
    /**
     * The gradient color.
     */
    private String rgb;

    /**
     * Create a circular gradient image in a bounded square.
     *
     * @param rgb The gradient color.
     */
    public CircularGradientImageRenderer(String rgb) {
        this.rgb = rgb;
    }

    /**
     * Pull out a substring without returning null.
     *
     * @param value The value.
     * @param begin The beginning index.
     * @param end   The ending index.
     * @return The substring.
     */
    private static String safeSubstring(String value, int begin, int end) {
        String sub = "";

        if (value != null) {
            if (value.length() > begin) {
                end = Math.min(end, value.length() - 1);
                sub = value.substring(begin, end + 1);
            }
        }

        return sub;
    }

    /**
     * Parse a hex value without throwing an exception.
     *
     * @param value The value to convert.
     * @return The integer value.
     */
    private static int safeHexParse(String value) {
        try {
            return Integer.parseInt(value, 16);
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    /**
     * Convert a string hex color into a Java Color object.
     *
     * @param rgb The string value.
     * @return The Color object.
     */
    public static Color stringToRGB(String rgb) {
        int r = 0;
        int g = 0;
        int b = 0;

        if (rgb != null && rgb.startsWith("#")) {
            r = safeHexParse(safeSubstring(rgb, 1, 2));
            g = safeHexParse(safeSubstring(rgb, 3, 4));
            b = safeHexParse(safeSubstring(rgb, 5, 6));
        }

        return new Color(r, g, b);
    }

    public void render(Graphics2D g2d, Rectangle2D rect) throws JRException {
        // Save the Graphics2D affine transform
        AffineTransform savedTrans = g2d.getTransform();

        float radius = (float) (Math.max(rect.getHeight(), rect.getWidth()) / 2);
        float[] fractions = {0.0f, 0.3f, 1.0f};
        Color[] colors = {Color.WHITE, Color.WHITE, stringToRGB(rgb)};

        // Paint a nice background...
        g2d.setPaint(new RadialGradientPaint((float) rect.getCenterX(), (float) rect.getCenterY(), radius, fractions, colors));
        g2d.fillRect(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
        g2d.draw(rect);

        // Restore the Graphics2D affine transform
        g2d.setTransform(savedTrans);
    }
}

This simple class is provided with a drawing rectangle and context by JasperReports. It uses the Java RadialGradientPaint class to draw the circular gradient. This class was added in Java 6 so don’t go looking for it in earlier Java versions.

To make this work in iReport, you create two items. The first item is the background image. Drag an image widget from the tool palette to the report. iReport will prompt you for an image to display in the widget. Press cancel. We’re dynamically creating the widget so we don’t need an image file. Place the image widget wherever you want the value displayed, and size it appropriately.

Next, select the Image Expression property and press the ellipsis button next to the field. A pop-up window will appear. In that window, type the name of the Java class that will dynamically create the gradient background. The class we’re using takes an RGB color as a String parameter, so a red gradient would be “#FF0000”.

Next, set the Expression Class property. Set the property to ‘net.sf.jasperreports.engine.JRRenderable’. This tells JasperReports that the custom class is writing a rendered image. You could also write a Java class that created a ‘java.awt.Image’. But that is a fixed-pixel size image that won’t scale appropriately in a PDF.

Finally, we drag a Text Field widget onto the report. Place it on top of the image widget created earlier and size it identically. If you find that the text field ends up behind the image, select the image, right click it and choose Send to Back.

Don’t look at the raw XML trying to find a z-order attribute, you won’t find one. JasperReports represents z-order by the order elements appear in the XML file. Elements that appear last overwrite elements that appear earlier in the file.

That’s all there is to it. You can change the size of the inner white circle by tweaking the Java class.

float[] fractions = {0.0f, 0.3f, 1.0f};

Move the center value (0.3f) up and down to find the right size.

One last tip. If you try to preview the report in iReport you might receive errors that the class can’t be found. Bring up the settings in iReport and add the path to the compiled class in the Classpath section.