Charts
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Baseline date: 2026-10-09. A sales report without a chart is a table that someone has to read twice. The monthly sales
report of the Bookshelf Billing service puts revenue by category, its share of the total, the monthly trend and a
category profile on one page, and this page shows how, with JasperReports 7.0.8. JasperReports does not draw charts
itself. Its chart element is a thin layer that turns report data into a JFreeChart object, asks JFreeChart to draw it,
and places the result in the document as an image element. Knowing where that layer ends makes the rest
straightforward: what the template can say is limited to what the layer exposes, and anything beyond it is done with a
theme, a customizer or a chart drawn by the application and passed in as an image.
The examples were compiled and run with Java 21 and JasperReports 7.0.8 in a plain Maven project, and the resulting
PDF pages were inspected. What the run showed, including the errors that the engine reports for the most common
mistakes, is marked as such. The template format is the JasperReports 7 JRXML, with kind on each element, as in
JRXML Report Design. The Maven setup inside a Spring Boot
service is in Getting Started with Spring Boot.
What a chart is in JasperReports
A chart is a report element with kind="chart" and a chartType. The element has three parts that are worth keeping
apart, because they live in different places in the template and are filled at different moments:
-
the element itself, with its position, its title, subtitle and legend, its render type and its optional theme and customizer;
-
the dataset, which says where the values come from: which expressions give the series, the category and the value, and when the dataset is reset and incremented while the report data is being read;
-
the plot, which says how the values are drawn: axis labels, tick marks, item labels, series colours and the settings that belong to one kind of chart only.
When the report is filled, the dataset collects the values, the engine builds a JFreeChart object from them through a chart theme, lets a customizer change it if one is attached, and stores the outcome in the document as an image whose renderer knows how to draw the JFreeChart object. The exporter then draws that image into the PDF. The official sample page describes the same idea: the library integrates charts produced by other libraries, and the chart element simplifies that integration.
Charts live in the optional jasperreports-charts artifact, which brings JFreeChart and the Batik SVG classes as
dependencies. The artifact’s own POM for 7.0.8 depends on JFreeChart 1.5.6, so a source that says 1.5.4 is out of date.
The engine alone does not know the chart element. In the run, a template with a chart element compiled on a classpath
without jasperreports-charts failed with Jackson’s Could not resolve type id 'chart' error and an empty list of
known type ids, and nothing in the message names the missing artifact. If a chart template stops loading after a
dependency clean-up, this is the cause.
Maven setup
The charts module is added next to the PDF exporter, the fonts and the expression compiler already used by the other
pages of this section. The last two entries are described later: jasperreports-chart-customizers holds ready-made
customizers and is optional, and XChart is only needed for the last example.
<properties>
<maven.compiler.release>21</maven.compiler.release>
<jasperreports.version>7.0.8</jasperreports.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-charts</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-pdf</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-fonts</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-jdt</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-chart-customizers</artifactId>
<version>${jasperreports.version}</version>
</dependency>
<dependency>
<groupId>org.knowm.xchart</groupId>
<artifactId>xchart</artifactId>
<version>4.0.4</version>
</dependency>
</dependencies>
JFreeChart and Batik come in transitively through jasperreports-charts, so they need no declaration, and the
customizer and theme examples below compiled against the JFreeChart classes obtained that way.
A complete example: four charts on one page
The data for the report is a list of sales, with a second list of monthly totals for the time series. Each row is a
JavaBean. A Java record does not work as a bean data source row: the run failed with
Error retrieving field value from bean: category and the cause
NoSuchMethodException: Unknown property 'category', because the bean data source looks for getter methods and a
record’s accessors are named category() rather than getCategory(). The Sale class below is a plain class with
getters.
package bookshelf;
import java.math.BigDecimal;
import java.util.Date;
public class Sale {
private final String category;
private final Date saleDate;
private final BigDecimal amount;
public Sale(String category, Date saleDate, BigDecimal amount) {
this.category = category;
this.saleDate = saleDate;
this.amount = amount;
}
public String getCategory() { return category; }
public Date getSaleDate() { return saleDate; }
public BigDecimal getAmount() { return amount; }
}
package bookshelf;
import java.math.BigDecimal;
import java.util.Date;
public class MonthTotal {
private final Date month;
private final BigDecimal revenue;
public MonthTotal(Date month, BigDecimal revenue) {
this.month = month;
this.revenue = revenue;
}
public Date getMonth() { return month; }
public BigDecimal getRevenue() { return revenue; }
}
The template has a group by category, a variable that totals the amount per category, a detail line per sale and four
charts in the summary band: a bar chart, a pie chart, a time series fed from a separate dataset, and a spider chart. It
is saved as src/main/resources/reports/sales-charts.jrxml. The next sections explain each part. The title and the
summary are bands that hold their elements directly in the version 7 format, so the summary has a single height and no
nested band: a nested band element was rejected in the run with Unrecognized field "band".
<jasperReport name="sales-charts" language="java" pageWidth="595" pageHeight="842"
columnWidth="515" leftMargin="40" rightMargin="40" topMargin="40" bottomMargin="40">
<dataset name="monthly">
<field name="month" class="java.util.Date"/>
<field name="revenue" class="java.math.BigDecimal"/>
</dataset>
<parameter name="MONTHLY" class="net.sf.jasperreports.engine.JRDataSource"/>
<field name="category" class="java.lang.String"/>
<field name="saleDate" class="java.util.Date"/>
<field name="amount" class="java.math.BigDecimal"/>
<variable name="categoryTotal" calculation="Sum" resetType="Group" resetGroup="category" class="java.math.BigDecimal">
<expression><![CDATA[$F{amount}]]></expression>
</variable>
<group name="category">
<expression><![CDATA[$F{category}]]></expression>
</group>
<title height="40">
<element kind="staticText" x="0" y="0" width="515" height="30" fontSize="18" bold="true">
<text><![CDATA[Bookshelf sales, third quarter]]></text>
</element>
</title>
<detail>
<band height="18">
<element kind="textField" x="0" y="0" width="200" height="16">
<expression><![CDATA[$F{category}]]></expression>
</element>
<element kind="textField" x="200" y="0" width="150" height="16" pattern="yyyy-MM-dd">
<expression><![CDATA[$F{saleDate}]]></expression>
</element>
<element kind="textField" x="350" y="0" width="165" height="16" hTextAlign="Right" pattern="#,##0.00">
<expression><![CDATA[$F{amount}]]></expression>
</element>
</band>
</detail>
<summary height="490">
<element kind="chart" chartType="bar" x="0" y="10" width="250" height="230" evaluationTime="Report">
<titleExpression><![CDATA["Revenue by category"]]></titleExpression>
<dataset kind="category" incrementType="Group" incrementGroup="category">
<series>
<seriesExpression><![CDATA["Revenue"]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$V{categoryTotal}]]></valueExpression>
</series>
</dataset>
<plot>
<categoryAxisLabelExpression><![CDATA["Category"]]></categoryAxisLabelExpression>
<valueAxisLabelExpression><![CDATA["EUR"]]></valueAxisLabelExpression>
</plot>
</element>
<element kind="chart" chartType="pie" x="265" y="10" width="250" height="230" evaluationTime="Report">
<titleExpression><![CDATA["Share of revenue"]]></titleExpression>
<dataset kind="pie" incrementType="Group" incrementGroup="category">
<series>
<keyExpression><![CDATA[$F{category}]]></keyExpression>
<valueExpression><![CDATA[$V{categoryTotal}]]></valueExpression>
</series>
</dataset>
<plot labelFormat="{0} {2}" legendLabelFormat="{0}"/>
</element>
<element kind="chart" chartType="timeSeries" x="0" y="250" width="250" height="230" evaluationTime="Report">
<titleExpression><![CDATA["Revenue by month"]]></titleExpression>
<dataset kind="timeSeries" timePeriod="Month">
<datasetRun subDataset="monthly">
<dataSourceExpression><![CDATA[$P{MONTHLY}]]></dataSourceExpression>
</datasetRun>
<series>
<seriesExpression><![CDATA["Revenue"]]></seriesExpression>
<timePeriodExpression><![CDATA[$F{month}]]></timePeriodExpression>
<valueExpression><![CDATA[$F{revenue}]]></valueExpression>
</series>
</dataset>
<plot>
<timeAxisLabelExpression><![CDATA["Month"]]></timeAxisLabelExpression>
<valueAxisLabelExpression><![CDATA["EUR"]]></valueAxisLabelExpression>
</plot>
</element>
<element kind="component" x="265" y="250" width="250" height="230">
<component kind="spiderChart">
<chartSettings showLegend="true">
<titleExpression><![CDATA["Category profile"]]></titleExpression>
</chartSettings>
<dataset incrementType="Group" incrementGroup="category">
<series>
<seriesExpression><![CDATA["Q3"]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$V{categoryTotal}]]></valueExpression>
</series>
</dataset>
<plot>
<maxValueExpression><![CDATA[1500d]]></maxValueExpression>
</plot>
</component>
</element>
</summary>
</jasperReport>
The program compiles the template, passes the second data source as a parameter, fills the report from the sale
list and writes the PDF. It sets REPORT_LOCALE explicitly: without it, the time series axis uses the locale of the
machine, and in a run on a Spanish-language workstation the month labels came out as ago-2026 and sept-2026.
package bookshelf;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
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;
public class ChartsReport {
static Date day(int year, int month, int day) {
return Date.from(LocalDate.of(year, month, day).atStartOfDay().toInstant(ZoneOffset.UTC));
}
public static void main(String[] args) throws Exception {
List<Sale> sales = List.of(
new Sale("Fiction", day(2026, 7, 5), new BigDecimal("420.50")),
new Sale("Fiction", day(2026, 8, 9), new BigDecimal("380.00")),
new Sale("Fiction", day(2026, 9, 14), new BigDecimal("510.25")),
new Sale("Science", day(2026, 7, 11), new BigDecimal("250.00")),
new Sale("Science", day(2026, 8, 20), new BigDecimal("310.75")),
new Sale("Science", day(2026, 9, 2), new BigDecimal("290.00")),
new Sale("History", day(2026, 7, 19), new BigDecimal("150.00")),
new Sale("History", day(2026, 8, 3), new BigDecimal("175.40")),
new Sale("History", day(2026, 9, 25), new BigDecimal("210.00")),
new Sale("Children", day(2026, 7, 28), new BigDecimal("330.10")),
new Sale("Children", day(2026, 8, 16), new BigDecimal("360.00")),
new Sale("Children", day(2026, 9, 7), new BigDecimal("405.90")));
List<MonthTotal> months = List.of(
new MonthTotal(day(2026, 7, 1), new BigDecimal("1150.60")),
new MonthTotal(day(2026, 8, 1), new BigDecimal("1226.15")),
new MonthTotal(day(2026, 9, 1), new BigDecimal("1416.15")));
JasperReport report;
try (InputStream in = ChartsReport.class.getResourceAsStream("/reports/sales-charts.jrxml")) {
report = JasperCompileManager.compileReport(in);
}
Map<String, Object> params = new HashMap<>();
params.put(JRParameter.REPORT_LOCALE, Locale.ENGLISH);
params.put("MONTHLY", new JRBeanCollectionDataSource(months));
JasperPrint print = JasperFillManager.fillReport(report, params, new JRBeanCollectionDataSource(sales));
Path out = Path.of("target/out");
Files.createDirectories(out);
JasperExportManager.exportReportToPdfFile(print, out.resolve("sales-charts.pdf").toString());
System.out.println("pages=" + print.getPages().size());
}
}
The result is one page: the title, the twelve sale lines and the four charts in a two by two grid. The bar and pie
charts show Fiction as the largest category, and the spider chart draws one polygon for the series Q3 with one axis per
category. The template follows the official
charts sample, whose reports use the same
element kind="chart" syntax.
Chart types and datasets
The chartType attribute selects the kind of chart, and each kind works with one kind of dataset, selected by the kind
attribute of the dataset element. The official documentation lists the values below.
chartType |
Dataset kind |
What it draws |
|---|---|---|
|
|
One slice per key. |
|
|
Bars grouped by category, one bar per series. The stacked types pile the series on top of each other. |
|
|
The same data as lines or filled areas over categories. |
|
|
Points given by an X and a Y value, joined by lines, filled, or left alone. |
|
|
Bars on a numeric or date axis. |
|
|
Points whose size comes from a third value. |
|
|
A date axis. The |
|
|
Stock-style charts with a high, a low, an open and a close value. Only one series is supported, and the volume is used by the candlestick chart only. |
|
|
A single value on a dial or a thermometer, with ranges and colours defined in the plot. |
|
several |
Several range axes that share one domain axis. |
|
|
Tasks over time. It appears in the sample reports and in the dataset kind list, but not in the chart type list of the sample page. |
Each dataset kind has its own series expressions. The element names below are the ones the official documentation
lists. In the sample JRXML files the XY expressions are spelled xvalueExpression and yvalueExpression, with a
lowercase v, which differs from the capitalisation in the prose of the sample page.
| Dataset kind | Expressions of a series |
|---|---|
|
|
|
|
|
|
|
The |
|
|
|
|
|
|
|
A single |
The pie dataset has a rule of its own. According to the configuration reference, a repeated key is an error unless the
property net.sf.jasperreports.chart.pie.ignore.duplicated.key is true, in which case only the last value for the key
is considered. A pie fed one slice per sale row, with the category as the key, runs into this as soon as two sales share a
category, which is why the example pie is fed from the group total instead.
When the dataset is incremented
A chart dataset behaves like a report variable: it is reset at some moment, incremented at others, and holds the
accumulated value when the chart is drawn. Four attributes of the dataset element control that: resetType,
resetGroup, incrementType and incrementGroup. Two examples show the effect.
With no incrementType, the dataset is incremented for every record. That is the right choice when each record is one
item of the chart. The stacked bar below adds one item per sale: the series is the month, the category is the book
category and the value is the amount. It ran as shown, with the three months stacked inside each category.
<element kind="chart" chartType="stackedBar" x="0" y="10" width="515" height="240"
evaluationTime="Report" showLegend="true" legendPosition="Right">
<titleExpression><![CDATA["Revenue by month and category"]]></titleExpression>
<dataset kind="category">
<series>
<seriesExpression><![CDATA[new java.text.SimpleDateFormat("yyyy-MM").format($F{saleDate})]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$F{amount}]]></valueExpression>
</series>
</dataset>
<plot orientation="Horizontal" showTickLabels="true" showTickMarks="true">
<seriesColor order="0" color="#264653"/>
<seriesColor order="1" color="#2a9d8f"/>
<seriesColor order="2" color="#e9c46a"/>
<categoryAxisLabelExpression><![CDATA["Category"]]></categoryAxisLabelExpression>
<valueAxisLabelExpression><![CDATA["EUR"]]></valueAxisLabelExpression>
</plot>
</element>
With incrementType="Group" and an incrementGroup, the dataset is incremented only when that group changes, and the
value it reads is the one accumulated by a variable. That is how the bar chart of the full example shows one bar per
category: the variable categoryTotal sums the amount and resets when the group changes, and the dataset takes one
reading per category. The two forms are not interchangeable. In a run where a single series Revenue received one item
per sale, with two sales of 100 and 50 in Fiction, the Fiction bar showed 50: a repeated series and category pair
replaces the earlier value instead of adding to it. Totals have to come from a variable or from the query. The groups
are explained in
Groups, Sorting & Filtering, and the
variables in
Expressions, Parameters, Fields & Variables.
The evaluationTime of the element decides when the chart is drawn. The default is Now, which draws it when the
engine reaches the element. The charts that show totals of the whole report are given evaluationTime="Report", so
that they are evaluated once the data has been read, and a chart in a group header that shows the group’s totals uses
Group with an evaluationGroup, as the official bar chart sample does.
The references for the types and datasets are the
charts sample and the
JRChart API.
A chart fed from another dataset
The time series chart in the example does not use the report’s rows. It reads monthly totals from a subdataset
declared at the top of the template, and a datasetRun inside the chart’s dataset binds it to a data source passed as
the MONTHLY parameter. This is the mechanism that subreports and tables use, described in
Subreports, Datasets, Tables & Lists,
and it is the way to chart data that is not the data being listed on the page: totals computed by another query, a
comparison with the previous year, or a series from another service. A datasetRun can take a connection expression
instead of a data source expression when the subdataset has its own query.
Titles, legends, plots and series colours
In the JasperReports 7 JRXML, the title, the subtitle and the legend are not child elements of the chart, as the
prose of the sample page still says. They are attributes and expressions on the element itself: titleExpression,
subtitleExpression, titleFont, titleColor, titlePosition, showLegend, legendPosition, legendColor,
legendBackgroundColor and legendFont. A template that uses a chartLegend child element is rejected when it is
loaded. The run reported Unrecognized field "chartLegend" and listed the fifty-three properties that the chart
element does know, which is a convenient reference of its own. The position values are the edges: Top, Bottom,
Left and Right.
The plot element holds what depends on the kind of chart. The settings common to all plots are backcolor,
orientation (Horizontal or Vertical), backgroundAlpha and foregroundAlpha, plus the seriesColor children
that set the colour of each series by its position. In version 7 the attribute of a seriesColor is order, not
seriesOrder: the run rejected seriesOrder and named color and order as the only two properties. The settings that
belong to one kind of chart are:
-
bar and line charts:
showTickMarksandshowTickLabels,showLinesandshowShapeson line charts, the axis label expressions (categoryAxisLabelExpressionandvalueAxisLabelExpression, ortimeAxisLabelExpressionfor a date axis), axis colours and fonts, anditemLabelto print the values on the items; -
pie charts:
labelFormatandlegendLabelFormat, which take JFreeChart’s{0}(key),{1}(value) and{2}(percentage) placeholders, as in the example’s{0} {2}; -
meter and thermometer charts: the dial shape, the scale, the units, the ranges and their colours, and the value display;
-
spider charts: described in their own section below.
Narrow charts clip long labels. In the run, the pie labels Children, Fiction and Science wrapped onto two lines
inside their label boxes at 250 points wide, and the bar chart abbreviated the category Children to Chil... on the
axis. Giving the chart more width, using a shorter label, or setting labelFormat to {0} alone are the usual
fixes. A chart drawn at a fixed size does not reflow with the page.
A few axis settings are properties of the chart element rather than attributes:
net.sf.jasperreports.chart.range.axis.tick.interval, net.sf.jasperreports.chart.range.axis.tick.count and
net.sf.jasperreports.chart.range.axis.integer.unit, and their domain.axis counterparts. The configuration reference
gives their scope as the chart. They are set with a property child of the chart element, and the integer-unit flag is
for count charts whose axis would otherwise show fractional ticks.
Items of a chart can carry hyperlinks: the pie series takes a sectionHyperlink and the category and XY series take an
itemHyperlink, with the same hyperlink type and expressions as other elements. A pie with a sectionHyperlink of type
Reference filled and exported in the run; the link annotations in the PDF were not inspected. The hyperlink types,
bookmarks and the way they are exported are covered in
Hyperlinks, Bookmarks & Books.
The attributes above are those of
JRChart, and the plot settings are
described in the charts sample.
Render types
A chart is drawn into the document by a renderer, and the renderType attribute chooses which one. The official
documentation gives three values:
-
draw: the chart is drawn directly on the target graphics context with the JFreeChart API; -
image: an image is produced first from the chart, and that image is drawn; -
svg: the chart is transformed into Scalable Vector Graphics, and that is drawn.
The default is draw, and it can be changed for every chart through the configuration property
net.sf.jasperreports.chart.render.type, at global, context or report scope. In the run, a chart with
renderType="svg" filled and exported to PDF like the others, with the Batik classes that jasperreports-charts
already brings. Which type is sharpest in a given output format was not compared. When a chart looks soft in the PDF,
the render type is the first attribute to try. The PDF exporter itself, and how it treats images, is the subject of
PDF Exporter Configuration.
Chart themes
A chart theme is the part of the engine that creates the JFreeChart object from the report’s chart definition, and it
decides the look of everything the template does not set: fonts, colours, backgrounds, gridlines, shadows. The default
theme is the one used in the examples above, and it is why the pie chart in the first run has a drop shadow. A theme
is selected for a whole deployment or for a report with the property net.sf.jasperreports.chart.theme, whose default
is default, and for one chart with the theme attribute of the element.
A custom theme implements net.sf.jasperreports.charts.ChartTheme, whose only method receives a ChartContext and
returns a JFreeChart. The simplest custom theme extends DefaultChartTheme, lets it build the chart, and restyles the
result. This one gives the Bookshelf reports a flat white style, a fixed colour per category on pies and a teal colour
on bars.
package bookshelf;
import java.awt.Color;
import java.awt.Font;
import java.util.Map;
import net.sf.jasperreports.charts.ChartContext;
import net.sf.jasperreports.charts.fill.DefaultChartTheme;
import net.sf.jasperreports.engine.JRException;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.Plot;
/** Starts from the default theme, then gives every chart the same house style. */
public class BookshelfChartTheme extends DefaultChartTheme {
private static final Color PETROL = new Color(0x264653);
private static final Color TEAL = new Color(0x2a9d8f);
private static final Map<String, Color> CATEGORY_COLORS = Map.of(
"Fiction", PETROL,
"Science", TEAL,
"History", new Color(0xe9c46a),
"Children", new Color(0xf4a261));
@Override
public JFreeChart createChart(ChartContext chartContext) throws JRException {
JFreeChart chart = super.createChart(chartContext);
chart.setBackgroundPaint(Color.WHITE);
if (chart.getTitle() != null) {
chart.getTitle().setFont(new Font("DejaVu Sans", Font.BOLD, 12));
}
Plot plot = chart.getPlot();
plot.setOutlineVisible(false);
if (plot instanceof PiePlot pie) {
pie.setShadowPaint(null);
CATEGORY_COLORS.forEach(pie::setSectionPaint);
} else if (plot instanceof CategoryPlot category) {
category.getRenderer().setSeriesPaint(0, TEAL);
}
return chart;
}
}
Themes are found through the extensions mechanism. A ChartThemeBundle gives names to one or more themes, and an
ExtensionsRegistryFactory provides the bundle to the engine. The factory is registered in a
jasperreports_extension.properties file at the root of the classpath, so a jar that holds these classes and that
file adds the theme to any application it is dropped into, without any other configuration.
package bookshelf;
import net.sf.jasperreports.charts.ChartTheme;
import net.sf.jasperreports.charts.ChartThemeBundle;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.extensions.ExtensionsRegistry;
import net.sf.jasperreports.extensions.ExtensionsRegistryFactory;
import net.sf.jasperreports.extensions.SingletonExtensionRegistry;
public class BookshelfChartThemeRegistryFactory implements ExtensionsRegistryFactory {
private static final ChartTheme THEME = new BookshelfChartTheme();
private static final ChartThemeBundle BUNDLE = new ChartThemeBundle() {
@Override
public String[] getChartThemeNames() {
return new String[] {"bookshelf"};
}
@Override
public ChartTheme getChartTheme(String themeName) {
return "bookshelf".equals(themeName) ? THEME : null;
}
};
@Override
public ExtensionsRegistry createRegistry(String registryId, JRPropertiesMap properties) {
return new SingletonExtensionRegistry<>(ChartThemeBundle.class, BUNDLE);
}
}
The registration file has one line. The segment after registry.factory. is an identifier chosen by the author of the
extension, and it only has to be unique among the registrations.
net.sf.jasperreports.extension.registry.factory.bookshelf.chart.themes=bookshelf.BookshelfChartThemeRegistryFactory
The template then names the theme, on one element or for the whole report:
<!-- for one chart -->
<element kind="chart" chartType="pie" theme="bookshelf" x="265" y="10" width="250" height="230" evaluationTime="Report">
...
</element>
<!-- or for every chart in the report, as a direct child of jasperReport -->
<property name="net.sf.jasperreports.chart.theme" value="bookshelf"/>
Both forms ran. With the report-level property, the bar, pie and time series charts took the theme’s white background and bold title font, and the pie got the four category colours and lost its shadow. The spider chart did not change, because it is a component and not a chart element. Its look is controlled by its own settings and customizer, as described below.
The official documentation also describes two XML-driven theme implementations, one on Spring beans and one on Castor
XML mapping, with sample themes named aegean, eye.candy.sixties and default.spring. They belong to the separate
artifact jasperreports-chart-themes, which exists on Maven Central at version 7.0.8. It was not run for this page, and
its registration steps, which the sample page describes as editing lines of a jasperreports_extension.properties
file, should be taken from the sample page itself. For most applications a small ChartTheme subclass like the one
above is simpler than either.
The sources are the chart themes sample
and the API pages of
ChartTheme and
ChartThemeBundle.
Chart customizers
A customizer changes one chart after the engine has created it. It receives the finished JFreeChart and the chart’s
definition, and can call any JFreeChart method on it, which makes it the tool for what neither the template nor the
theme exposes: item labels on every bar, a marker line at a target value, a fixed axis range, a different renderer.
The official documentation recommends themes over customizers for a look that applies to many charts, and keeps
customizers for adjustments that belong to a single chart.
The interface is net.sf.jasperreports.charts.JRChartCustomizer, with one method, and the convenient base class is
JRAbstractChartCustomizer, which also gives access to the properties set on the chart element. In 7.0.8 both are in
the net.sf.jasperreports.charts package of the charts module, so imports in code written for an earlier version
should be checked. For the spider chart component the interface is
net.sf.jasperreports.components.charts.ChartCustomizer. This customizer draws the value on top of each bar and takes
the bar colour from a property of the chart element.
package bookshelf;
import java.awt.Color;
import net.sf.jasperreports.charts.JRAbstractChartCustomizer;
import net.sf.jasperreports.charts.JRChart;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BarRenderer;
/** Highlights the bars and draws their values; the bar colour comes from a chart property. */
public class RevenueBarCustomizer extends JRAbstractChartCustomizer {
@Override
public void customize(JFreeChart chart, JRChart jasperChart) {
String colour = getProperty("barColor");
CategoryPlot plot = chart.getCategoryPlot();
BarRenderer renderer = (BarRenderer) plot.getRenderer();
renderer.setSeriesPaint(0, colour != null ? Color.decode(colour) : new Color(0x1f77b4));
renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setDefaultItemLabelsVisible(true);
renderer.setShadowVisible(false);
plot.getRangeAxis().setUpperMargin(0.15);
}
}
There are two ways to attach it. The customizerClass attribute names the class and is the shortest form, but it gives
the customizer no properties, so getProperty returns null and the code above falls back to its default blue; the run
showed blue bars with this form. The more flexible way is a pair of properties on the chart element:
net.sf.jasperreports.customizer.class.<name> holds the class, and the properties named
net.sf.jasperreports.customizer.<name>.<property> are the ones the customizer reads with getProperty("<property>").
The configuration reference lists both patterns, and the official customizers sample attaches several customizers,
each with its own name, to one chart. The red bars in the run came from this form:
<element kind="chart" chartType="bar" x="0" y="10" width="250" height="230" evaluationTime="Report" renderType="svg">
<property name="net.sf.jasperreports.customizer.class.revenue" value="bookshelf.RevenueBarCustomizer"/>
<property name="net.sf.jasperreports.customizer.revenue.barColor" value="#c0392b"/>
<titleExpression><![CDATA["Revenue by category"]]></titleExpression>
<dataset kind="category" incrementType="Group" incrementGroup="category">
<series>
<seriesExpression><![CDATA["Revenue"]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$V{categoryTotal}]]></valueExpression>
</series>
</dataset>
<plot/>
</element>
The artifact jasperreports-chart-customizers ships ready-made customizers for common adjustments, configured only with
properties, so no Java is needed. The official customizers sample uses, among others,
net.sf.jasperreports.customizers.axis.RangeAxisCustomizer and DomainAxisCustomizer, a category marker customizer, a
legend shape customizer and a step customizer. The range axis one ran as shown, and fixed the value axis of a bar chart
from 0 to 3000:
<property name="net.sf.jasperreports.customizer.class.rangeAxis" value="net.sf.jasperreports.customizers.axis.RangeAxisCustomizer"/>
<property name="net.sf.jasperreports.customizer.rangeAxis.minValue" value="0"/>
<property name="net.sf.jasperreports.customizer.rangeAxis.maxValue" value="3000"/>
A customizer works on the JFreeChart object that the theme created, so one written against the default theme can break if a different theme changes the plot type or the renderer. Casting the renderer, as the example does, ties the customizer to the chart type it is attached to.
The references are the chart customizers sample
and the API pages of
JRChartCustomizer and
JRAbstractChartCustomizer.
The spider chart component
A spider chart, also called a radar chart, draws one axis per category and one polygon per series. It is not a chart
element but a component, declared inside element kind="component" with a component kind="spiderChart". According to
the official sample it was developed to show how a custom component can be built on JFreeChart. Its structure mirrors
the chart element, and is the one that ran in the example above:
-
chartSettingsholds the title, subtitle and legend settings, and the hyperlink expressions; -
datasetis a category dataset, with the sameseries,seriesExpression,categoryExpressionandvalueExpression; -
plotholds the spider-specific settings.
The plot attributes that the official sample documents are rotation (Clockwise or Anticlockwise), tableOrder
(Row or Column), isWebFilled, startAngle (the default is 90 degrees), headPercent, interiorGap,
axisLineColor, axisLineWidth, labelGap, labelColor, backcolor, backgroundAlpha and foregroundAlpha, a
labelFont child and a maxValueExpression child that fixes the maximum of every axis. The example sets only
maxValueExpression, at 1500d, so that the four categories share one scale. The code follows the
spider chart component sample,
and the element names are those of the
SpiderChartComponent API.
The sample states two limits: tick marks and tick labels are not supported on the axes, and negative values are not supported. A spider chart is a poor fit for a handful of categories, as the four-axis diamond in the example shows, and more useful with six or more.
Three-dimensional charts in version 7
The 3D chart types, bar3D, stackedBar3D and pie3D, are still accepted by the template in 7.0.8, but they no
longer draw a 3D effect. The reason is upstream: JFreeChart removed its 3D charts in version 1.5.0, and JasperReports 7 is
built on JFreeChart 1.5.6. The official samples carry a notice that the three types are deprecated and will be removed.
In the run, a bar3D chart filled and exported without error and came out as flat bars like those of bar, with no
depth. Old templates therefore keep working with a different look. The practical advice is to change bar3D to
bar, stackedBar3D to stackedBar and pie3D to pie in templates that are being maintained, so that the
deprecation does not become a failure on a later upgrade. Books and tutorials that show 3D charts were written for
JFreeChart 1.0, and their screenshots cannot be reproduced with the current version.
Charts drawn by the application: JFreeChart and XChart
The built-in element covers the common charts, and plot settings and customizers cover a lot of what it does not. When a chart needs something beyond that, such as a chart type JFreeChart supports but the element does not, or a library with a different look, the application can draw the chart itself and give the template an image. Two routes are documented in the official samples.
The first route keeps JFreeChart as the drawing engine but bypasses the element. An image element accepts a value of
type net.sf.jasperreports.renderers.Renderable, and JCommonDrawableRendererImpl in the charts module wraps a
JFreeChart Drawable as one, so the chart draws itself on the graphics context. The official sample does this in a
scriptlet. Passing the renderer as a parameter, as below, needs no scriptlet and keeps the chart code in the service.
The second route uses a library that produces a bitmap. XChart builds the chart and BitmapEncoder turns it into a
BufferedImage, which the template shows through an image element whose expression class is java.awt.Image.
package bookshelf;
import java.awt.image.BufferedImage;
import java.util.Map;
import net.sf.jasperreports.charts.renderers.JCommonDrawableRendererImpl;
import net.sf.jasperreports.renderers.Renderable;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.DefaultCategoryDataset;
import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.XYChart;
import org.knowm.xchart.XYChartBuilder;
public final class ChartImages {
private ChartImages() {
}
/** Fill parameters TREND (a Renderable) and XCHART (a java.awt.Image) for the template. */
public static Map<String, Object> parameters() throws Exception {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(1150.60, "Revenue", "Jul");
dataset.addValue(1226.15, "Revenue", "Aug");
dataset.addValue(1416.15, "Revenue", "Sep");
JFreeChart jfree = ChartFactory.createLineChart("JFreeChart renderer", "Month", "EUR", dataset);
Renderable trend = new JCommonDrawableRendererImpl(jfree);
XYChart xchart = new XYChartBuilder()
.width(500).height(460)
.title("XChart image").xAxisTitle("Month").yAxisTitle("EUR")
.build();
xchart.addSeries("Revenue", new double[] {7, 8, 9}, new double[] {1150.6, 1226.15, 1416.15});
BufferedImage image = BitmapEncoder.getBufferedImage(xchart);
return Map.of("TREND", trend, "XCHART", image);
}
}
The returned map is added to the fill parameters, next to the data source. The matching parts of the template declare the two parameters and place the image elements:
<parameter name="TREND" class="net.sf.jasperreports.renderers.Renderable"/>
<parameter name="XCHART" class="java.awt.Image"/>
<element kind="image" x="0" y="480" width="250" height="215" scaleImage="RetainShape">
<expression><![CDATA[$P{TREND}]]></expression>
</element>
<element kind="image" x="265" y="480" width="250" height="215" scaleImage="RetainShape">
<expression><![CDATA[$P{XCHART}]]></expression>
</element>
Both ran with XChart 4.0.4 and JFreeChart 1.5.6, and both images appeared on the page. The JFreeChart chart drew at the
size of its element with JFreeChart’s default fonts, which look large in a small box. The XChart image is a bitmap
rendered at the size given to the builder and scaled into the element, so it loses sharpness if it is enlarged. The
official XChart sample was written for the 3.x API; XChart 4.0.4 was the latest release on Maven Central on the baseline
date, and the example compiled against it with the XYChartBuilder and BitmapEncoder.getBufferedImage calls shown.
JFreeChart is published under the LGPL and XChart under the Apache License 2.0; both licences should be confirmed on the
project pages before a release.
The approaches compare as follows:
| Approach | Strength | Cost |
|---|---|---|
Built-in |
Declarative: data comes from datasets and groups, and the template stays self-contained. |
Limited to the types and settings the element exposes, drawn with the theme’s look. |
Theme and customizers |
Full access to the JFreeChart object for one chart or for all of them, with the data still declared in the template. |
Java code to maintain, tied to the JFreeChart version in use. |
JFreeChart |
Any JFreeChart chart, drawn by the engine’s own renderer. |
The data is prepared in Java, outside the template. |
XChart or another bitmap library |
A different style and a simple API. |
A bitmap, so sharpness depends on its size, and a further dependency. |
Charts are also possible in the iText-based pages of this section: a chart rendered to an image or an SVG with the same libraries can be placed in a document, as described in iText: Colors, Images & SVG.
The two routes follow the official JFreeChart sample and XChart sample.
Charts and accessibility
A chart in the PDF is graphics. Tagged PDF output is a Professional-edition feature in JasperReports 7.0.8, as the architecture page states, and what the community build produces for a chart for assistive technology was not examined here. The dependable approach in either edition is to put the data the chart shows next to it, as a table or in the text, as the detail lines in the example do. Where a PDF has to conform to an accessibility standard, the chart should be treated as a picture of data that is also available as text.
Differences from older material
Older tutorials and books on JasperReports charts were mostly written for versions 1 to 5, and their charts differ from what 7.0.8 does in these points.
-
In 7.0 the charts are in the optional
jasperreports-chartsartifact, which has to be declared. -
They are built on JFreeChart 1.5.6, not the 1.0 line that the older material describes, and the 3D charts no longer have their depth.
-
The JRXML is the version 7 format, in which the element is
element kind="chart", and the title and the legend are attributes. -
The customizer interfaces are in
net.sf.jasperreports.charts, and customizers can also be attached as properties, with ready-made classes injasperreports-chart-customizers. -
Themes, supported since version 3.1, are the recommended way to restyle charts across reports.
References
-
Configuration reference:
net.sf.jasperreports.chart.render.type -
Configuration reference:
net.sf.jasperreports.chart.pie.ignore.duplicated.key -
JasperReports Architecture & Lifecycle on this site
-
JRXML Report Design on this site
-
Groups, Sorting & Filtering on this site
-
Subreports, Datasets, Tables & Lists on this site
-
Getting Started with Spring Boot on this site