Using Dynamically Generated JFreeChart Charts and JasperReports
Join the DZone community and get the full member experience.
Join For FreeIf you are reading this blog it means you want to use JasperReport with dynamic programmatically generated images. An example of such use cases in generating complex charts using JFreeChart and then including these charts into reports.
Each report in JasperReport uses a datasource to populate the fields, we need to use JRBeanCollectionDataSource which can be considered as a simple collection of JavaBeans. Each object in the JRBeanCollectionDataSource can be used to populate one row of the report or it can be used to prepare calculated values and so on.
Each Report in JasperReport is basically an XML file which describe the report. in our case the report description document is like:
<?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="DataSourceReport" pageWidth="595" pageHeight="842" columnWidth="515" leftMargin="40" rightMargin="40" topMargin="50" bottomMargin="50">
<style name="Sans_Normal" isDefault="true" fontName="DejaVu Sans" fontSize="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false"/>
<style name="Sans_Bold" isDefault="false" fontName="DejaVu Sans" fontSize="12" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false"/>
<style name="Sans_Italic" isDefault="false" fontName="DejaVu Sans" fontSize="12" isBold="false" isItalic="true" isUnderline="false" isStrikeThrough="false"/>
<field name="image" class="java.awt.image.BufferedImage"/>
<field name="description" class="java.lang.String"/>
<detail>
<band height="180">
<image scaleImage="RetainShape" hAlign="Center" isUsingCache="true" isLazy="true">
<reportElement x="67" y="17" width="344" height="140"/>
<imageExpression class="java.awt.Image"><![CDATA[$F{image}]]></imageExpression>
</image>
<textField>
<reportElement x="83" y="157" width="311" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String"><![CDATA[$F{description}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
As you can see I defined two fields named image and description which are two property of the ReportBean objects included in the data source. The report looks as follow in the IreportDesigner (I am using NetBeans plugin for developing reports).

Lets see what is the JavaBean class we want to use to carry the report fields from our Java code to the JasperReport engine.
public class ChartBean {
public ChartBean(BufferedImage image, String description) {
setImage(image);
setDescription(description);
}
private java.awt.image.BufferedImage image;
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}
Now we need to prepare the datasource for the report, to prepare the datasource I wrote a method like the following snippet which uses another method to extract the BufferedImage from the JFreeChart items which we want to include in the report.
public JRBeanCollectionDataSource prepareDataSource() {
List charts = new ArrayList();
for (int i = 0; i < 5; i++) {
JFreeChart chart = createChart("This is chart number: " + i);
BufferedImage bi = extractImage(chart, 800, 600);
ChartBean chartBean = new ChartBean(bi, "This is description for Chart: " + i);
charts.add(chartBean);
}
return new JRBeanCollectionDataSource(charts);
}
The createChart method create some sample charts which we will include in the report. the method is simpley creating some dummy ring charts without any specific and meaningful data.
private JFreeChart createChart(String chartTitle) {
DefaultPieDataset piedataset = new DefaultPieDataset();
piedataset.setValue("One", new Double(43.200000000000003D));
piedataset.setValue("Two", new Double(10D));
piedataset.setValue("Three", new Double(27.5D));
piedataset.setValue("Four", new Double(17.5D));
piedataset.setValue("Five", new Double(11D));
piedataset.setValue("Six", new Double(19.399999999999999D));
JFreeChart jfreechart = ChartFactory.createRingChart(chartTitle, piedataset, false, true, false);
RingPlot ringplot = (RingPlot) jfreechart.getPlot();
ringplot.setLabelFont(new Font("SansSerif", 0, 12));
ringplot.setNoDataMessage("No data available");
ringplot.setSectionDepth(0.34999999999999998D);
ringplot.setCircular(false);
ringplot.setLabelGap(0.02D);
return jfreechart;
}
The other method which need to be explained is extractImage which simply create a BufferedImage from a given JfreeChart object.
public BufferedImage extractImage(JFreeChart chart, int width, int height) {
BufferedImage img =
new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = img.createGraphics();
chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));
g2.dispose();
return img;
}
Finally I have a method named showReport which uses the created datasource to prepare a report and then uses JRViewer to show the report. The code snippet is as follow:
public void showReport(JRBeanCollectionDataSource dataSource) {
try {
Map parameters = new HashMap();
JasperDesign jasperDesign = JRXmlLoader.load(this.getClass().getResourceAsStream("/jasperdynamic/report.jrxml"));
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
JRViewer viewer = new JRViewer(jasperPrint);
viewer.setSize(850, 500);
viewer.setMinimumSize(new Dimension(850, 510));
viewer.setPreferredSize(new Dimension(850, 510));
this.setLayout(new BorderLayout(10, 10));
this.setSize(850, 500);
this.getContentPane().add(viewer);
this.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
And the complete code for the application application class is as follow:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jasperdynamic;
import javax.swing.JFrame;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import net.sf.jasperreports.view.JRViewer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.RingPlot;
import org.jfree.data.general.DefaultPieDataset;
/**
*
* @author masoud
*/
public class Main extends JFrame {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Main m = new Main();
m.showReport(m.prepareDataSource());
}
public JRBeanCollectionDataSource prepareDataSource() {
List charts = new ArrayList();
for (int i = 0; i < 5; i++) {
JFreeChart chart = createChart("This is chart number: " + i);
BufferedImage bi = extractImage(chart, 800, 600);
ChartBean chartBean = new ChartBean(bi, "This is description for Chart: " + i);
charts.add(chartBean);
}
return new JRBeanCollectionDataSource(charts);
}
public BufferedImage extractImage(JFreeChart chart, int width, int height) {
BufferedImage img =
new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = img.createGraphics();
chart.draw(g2, new Rectangle2D.Double(0, 0, width, height));
g2.dispose();
return img;
}
public void showReport(JRBeanCollectionDataSource dataSource) {
try {
Map parameters = new HashMap();
JasperDesign jasperDesign = JRXmlLoader.load(this.getClass().getResourceAsStream("/jasperdynamic/report.jrxml"));
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
JRViewer viewer = new JRViewer(jasperPrint);
viewer.setSize(850, 500);
viewer.setMinimumSize(new Dimension(850, 510));
viewer.setPreferredSize(new Dimension(850, 510));
this.setLayout(new BorderLayout(10, 10));
this.setSize(850, 500);
this.getContentPane().add(viewer);
this.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private JFreeChart createChart(String chartTitle) {
DefaultPieDataset piedataset = new DefaultPieDataset();
piedataset.setValue("One", new Double(43.200000000000003D));
piedataset.setValue("Two", new Double(10D));
piedataset.setValue("Three", new Double(27.5D));
piedataset.setValue("Four", new Double(17.5D));
piedataset.setValue("Five", new Double(11D));
piedataset.setValue("Six", new Double(19.399999999999999D));
JFreeChart jfreechart = ChartFactory.createRingChart(chartTitle, piedataset, false, true, false);
RingPlot ringplot = (RingPlot) jfreechart.getPlot();
ringplot.setLabelFont(new Font("SansSerif", 0, 12));
ringplot.setNoDataMessage("No data available");
ringplot.setSectionDepth(0.34999999999999998D);
ringplot.setCircular(false);
ringplot.setLabelGap(0.02D);
return jfreechart;
}
}
Following image shows how the report will look like after we run the program. and the complete source code can be downloaded from Here

From http://weblogs.java.net/blog/kalali
Opinions expressed by DZone contributors are their own.
Comments