JasperReports Ultimate Guide - Samples - Schema - Configuration - Functions - FAQ - API (Javadoc)

JasperReports - Custom Visualization Components Sample (version master-SNAPSHOT)


Shows how JavaScript based visualizations could be included in reports using the Custom Visualization Component.

Download All Sample Source Files
Browse Sample Source Files on Git


Main Features in This Sample

Custom Visualization Components


top

Custom Visualization ComponentsDocumented by Giulio Toffoli


Description / Goal
Shows how to render a custom visualization in a report, using the custom visualization component shipped with the JasperReports library.

Since
6.7.0


Custom Visualization Component Overview

The Custom Visualization Component allows to render an SVG image generated by using JavaScript code.
Its main goal is to leverage JavaScript visualization libraries (such as d3.js) in order to produce content to be rendered in the report.
This component does not produce any content itself, instead it acts as bridge between the JasperReports library and the JavaScript module provided by the user, which at the end is responsible to produce the visualization.
In case the report is exported to HTML, the JavaScript code is executed directly inside the final document. This allows to take full advantage of the browser functionalities.
When the report is exported to other file formats (such as PDF or MS Word), the component takes care of rendering the SVG image produced by the JavaScript code, behaving like a regular image element.
If the script does not produce SVG image, but more complex HTML, the visualization can be rendered as PNG image when exported to formats different from HTML.

Custom Visualization Component Schema

Below is the main part of the custom visualization component schema. As any other component element, the evaluationTime and evaluationGroup attributes are provided:
<element name="customvisualization" substitutionGroup="jr:component">
<complexType>
<complexContent>
    <extension base="jr:componentType">
    <sequence>
        <element name="itemProperty" type="c:ItemProperty" minOccurs="0" maxOccurs="unbounded" />
        <element ref="cvc:cvData" minOccurs="0" maxOccurs="unbounded" />
    </sequence>
    <attribute name="evaluationTime" type="jr:basicEvaluationTime" use="optional" default="Now"/>
    <attribute name="evaluationGroup" type="string" use="optional"/>
    <attribute name="processingClass" type="string" use="optional"/>
    <attribute name="onErrorType" use="optional" default="Error">
        <simpleType>
        <restriction base="string">
            <enumeration value="Error"/>
            <enumeration value="Blank"/>
            <enumeration value="Icon"/>
        </restriction>
        </simpleType>
    </attribute>
    </extension>
</complexContent>
</complexType>
</element>
Main Properties

Since the custom visualization component works like a wrapper, its definition is very generic and based on simple properties (itemProperty). There are four common properties that can be used with any custom visualization component:
  • script - the location of the JavaScript file implementing the component
  • module - (optional) the name of the JavaScript module to be loaded by using RequireJs (if not specified, the base name of the JavaScript file without extensions will be assumed as name of the module)
  • css - (optional) the location of a CSS file eventually used by the component
  • renderAsPng - (optional) a boolean value that can be used when the content produced is not an SVG image
All the configured properties will be passed to the JavaScript function in charge of rendering the visualization.
The value for the properties can be either a static value or a value defined by means of an expression.

Configuring Data Series

One or more series of data may be provided to the script implementing the visualization by means of cvData elements:
<element name="cvData">
  <complexType>
    <sequence>
      <element ref="jr:dataset" minOccurs="0" maxOccurs="1"/>
      <element ref="cvc:item" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
  </complexType>
</element>
A cvData can collect its data either from a subdataset, or from a hardcoded list of cvc:item. Each cvc:item is defined by a set of ItemProperty elements.
The name of each item property to be set for each cvc:item depends by the component we are working with.

The Sparkline sample uses a subdataset to populate a simple series of values. In this case each cvc:item has a single ItemProperty called value.


Custom Visualization Component Samples

This sample contains the implementation of 6 custom visualizations:
  • Simple Rectangle - A very simple visualization to explain the basics of creating your own Custom Visualization Component.

  • D3 Rectanle - An improved version of Simple Rectangle which use d3.js. The component accepts a configuration parameter and displays vertical lines based on provided data.

  • Figures - A d3.js based visualization to represent a quantity by using figures.


  • Radial Progress - A d3.js based visualization to display a percentage value with a circle


  • Sparkline - A d3.js based minimalist line chart which displays data coming from a sub-dataset


  • LeafLet Markers - A map with markers implemented by using the LeafLet.js mapping library


The source code of the custom visualizations can be found in the demo/samples/customvisualization/components folder.
Each custom visualization is composed by one or more JavaScript files that are combined and optimized by using RequireJs.
This optimization process creates a single minified JavaScript file which represents the custom visualization component referenced inside the report template by means of the script property described above.

To generate the components from the sources, in a command prompt/terminal window set the current folder to demo/samples/customvisualization within the JasperReports source project and run the > ant components command.
The generated components will be saved in demo/samples/customvisualization/build/components, each in its own directory.

Creating Your Own Custom Visualization Component

To implement a new Custom Visualization Component we need to define a JavaScript module that returns an anonymous function implementing the component logic.
This module will be then optimized and combined with the JavaScript libraries used by the component (if any) with the RequireJs optimizer (r.js).
The build process is configured in a file called build.js in which dependencies, module name and other information are specified.
Before starting, it is important to keep in mind that Custom Visualization Components are not able to stretch, which means that the allocated space in the report for the component is defined at design time and will not change based on what is rendered. This is the same behavior of other elements such charts and images.
It is also important to notice that producing an SVG image provides much better results over complex HTML content, which would need to be rendered as a PNG image.

Let's create a simple component named simplerectangle which displays a simple SVG rectangle.
We start by creating a file called simplerectangle.js. The content of this file would be something like:
define('simplerectangle', [], function () {

  return function (instanceData) {

    var w = instanceData.width,   // The width of the element
        h = instanceData.height;  // The height of the element

    var container = window.document.getElementById(instanceData.id);

    // Create the SVG Document
    var svgDocument = window.document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svgDocument.setAttributeNS(null, "width", w);
    svgDocument.setAttributeNS(null, "height", h);

    // Create the SVG Rectangle
    var rectangle = window.document.createElementNS("http://www.w3.org/2000/svg", "rect");
    rectangle.setAttributeNS(null, "x", 0);
    rectangle.setAttributeNS(null, "y", 0);
    rectangle.setAttributeNS(null, "width",  w);
    rectangle.setAttributeNS(null, "height",  h);
    rectangle.setAttributeNS(null, "fill", "red");

    // Add the rectangle to the SVG
    svgDocument.appendChild( rectangle );
    
    // Add the SVG to the container
    container.appendChild( svgDocument );
  };
});
The instanceData object passed to our function holds all the properties and data series defined in the component configuration.
The simplest instanceData object would look like:
{  
   "module":"simplerectangle",
   "id":"element1412794598",
   "width":802,
   "height":475,
   "isInteractiveViewer":"false",
   "script_uri":"simplerectangle/simplerectangle.min.js",
   "series":[]
}

In order to generate the RequireJS module, we create a build.js file as follow:
({
  optimize: 'none',
  baseUrl: '',
  paths: {
    'simplerectangle': 'simplerectangle' 		
  }, 
  
  wrap: {
    start: "(function(root){\n\nvar define = root.define;\n\n",
    end: "\n\n}(typeof __visualize__ != 'undefined' ? __visualize__ : (typeof __jrio__ != 'undefined' ? __jrio__ : window)));"
  },

  name: "simplerectangle",
  out: "simplerectangle.min.js"
})
We list the files to combine in paths, in the form of
module: 'path_to_file' In this example we have only one file called basic.js, the extension .js is omitted.
The wrap option is used to be sure that this component will work properly in evnironments like JasperReports IO and JasperReports Server interactive viewers.

To generate the final component we can use different tools (i.e. Java or Node).
To compile the file using Node, be sure node is installed, then in a command prompt/terminal window set the current folder to the folder in which you created your component (i.e. demo/samples/customvisualization/components/basic within the JasperReports source project) then run the command:

> node ../../libraries/requirejs/r-2.3.5.js -o build.js

To compile the file using Java, the Rhino JavaScript engine is used. Here is the command to run:

> java -cp /path/to/rhino.jar org.mozilla.javascript.tools.shell.Main -opt -1 ../../libraries/requirejs/r-2.3.5.js -o build.js

The full reference of the build.js file is available at https://github.com/requirejs/r.js/blob/master/build/build.js

We will use this newly create component in a JRXML with the following code:
<componentElement>
  <reportElement x="0" y="80" width="802" height="475" uuid="ba29f458-9e56-4017-b65d-8de18ac7470e" />
  <cvc:customvisualization
        xmlns:cvc="http://www.jaspersoft.com/cvcomponent"
        xsi:schemaLocation="http://www.jaspersoft.com/cvcomponent http://www.jaspersoft.com/cvcomponent/component.xsd"
        evaluationTime="Report">
    <cvc:itemProperty name="module" value="simplerectangle"/>
    <cvc:itemProperty name="script" value="simplerectangle/simplerectangle.min.js"/>
  </cvc:customvisualization>
</componentElement>


Our component creates a rectangle by using pure JavaScript and currently it does not use any data coming from the report.
Let's improve it and start rendering real data. We will create a new component named d3rectangle and use a JavaScript library to simplify the way we generate our SVG code.
D3.js is an extremely powerful library to display data by generating SVG. We start by declaring a dependency to d3.js in our build.js file as follow:
({
  optimize: 'none',
  baseUrl: '',
  paths: {
    'd3': 'd3': '../../libraries/d3/d3-5.5.0.min',
    'd3rectangle': 'd3rectangle' 		
  }, 
  
  wrap: {
    start: "(function(root){\n\nvar define = root.define;\n\n",
    end: "\n\n}(typeof __visualize__ != 'undefined' ? __visualize__ : (typeof __jrio__ != 'undefined' ? __jrio__ : window)));"
  },

  name: "d3rectangle",
  out: "d3rectangle.min.js"
})
The D3 function needs to be specified in our module definition in the following way:
define('d3rectangle', ['d3'], function (d3) {

  return function (instanceData) {
            ...
At this point the d3 library is at our disposal. We will display a set of vertical lines inside our rectangle. To do that, we will need to provide some data to the component by means of series. We expect our series to have two fields:
  • value - which will be used to determine the horizontal position of our line
  • color - a string defining the color used to plot the line
We will also provide a property to set the background color for our rectangle, which we will call background.
Here is the code of our d3 based visualization:
define('d3rectangle', ['d3'], function (d3) {

  return function (instanceData) {

    var w = instanceData.width,   // The width of the element
        h = instanceData.height;  // The height of the element
        bgColor = instanceData.background; 

    // If no property called background has been provided, let's use a default gray background color.
    if (typeof bgColor === 'undefined') {
      bgColor = '#efefef';
    }

    // Add the SVG to the dom...
    var svg = d3.select('#' + instanceData.id).append('svg')
                  .attr('width', w)
                  .attr('height', h);

    // Add the rectangle...
    var rectangle = svg.append('rect')
                  .attr('x', 0)
                  .attr('y', 0)
                  .attr('width', w)
                  .attr('height', h)
                  .attr('fill', bgColor);

    // Define a scale to map the width of the component with the values range
    var hscale = d3.scaleLinear()
            .range([10, instanceData.width-10]) // we leave 10px padding on left and right
            .domain([
                d3.min(instanceData.series[0], function(d) { return +d.value; }),
                d3.max(instanceData.series[0], function(d) { return +d.value; })
              ]);

    // Add the lines
    svg.append("g").selectAll("line")
        .data(instanceData.series[0])
        .enter()
        .append('line')
        .attr('x1', function(d) {  return hscale(+d.value); } )
        .attr('y1', 0)
        .attr('x2', function(d) {  return hscale(+d.value); } )
        .attr('y2', h)
        .attr('stroke', function(d) { return d.color; })
        .attr('stroke-width', 3);             

  };
});
In order to use our new component, we will have to set the background property and define a sub-dataset to provide the values for the lines:
<componentElement>
  <reportElement x="0" y="80" width="802" height="475" uuid="ba29f458-9e56-4017-b65d-8de18ac7470e" />
  <cvc:customvisualization 
            xmlns:cvc="http://www.jaspersoft.com/cvcomponent"
            xsi:schemaLocation="http://www.jaspersoft.com/cvcomponent http://www.jaspersoft.com/cvcomponent/component.xsd"
            evaluationTime="Report">
    <cvc:itemProperty name="module" value="d3rectangle"/>
    <cvc:itemProperty name="script" value="d3rectangle/d3rectangle.min.js"/>
    <cvc:itemProperty name="background" value="#eeeeee"/>
    <cvc:cvData>
      <dataset>
        <datasetRun subDataset="Sample Dataset" uuid="0e1d87a7-9376-4927-9b1e-30835421a98a">
          <dataSourceExpression>new net.sf.jasperreports.engine.data.JsonDataSource( 
                new StringBufferInputStream( 
                    "[{ value: 20, color: 'red'}," +
                     "{ value: 35, color: 'green'}," +
                     "{ value: 78, color: 'blue'}," +
                     "{ value: 92, color: 'yellow'}]") )
           </dataSourceExpression>
        </datasetRun>
      </dataset>
      <cvc:item>
        <cvc:itemProperty name="value" value="0">
          <valueExpression>$F{value}</valueExpression>
        </cvc:itemProperty>
        <cvc:itemProperty name="color" value="0">
          <valueExpression>$F{color}</valueExpression>
        </cvc:itemProperty>
      </cvc:item>
    </cvc:cvData>
  </cvc:customvisualization>
</componentElement>


Custom Visualization JRXML Samples

This sample contains 6 JRXML files, one for each custom visualization component
  1. Simple Rectangle

    This report template contains the Simple Rectangle component described in the section above, which creates a simple rectangle without displaying any specific data.
  1. D3 Rectangle

    This report template contains the D3 Rectangle component described in the section above, which creates a rectangle with a configurable background color and a set of lines provided by means on a sub-dataset.
  1. Figures

    This report template shows how to use the Figures component with different configuration options.
    The report does not use any data; for each component a static value is set for the property "itemsValue".
  1. Radial Progress

    This report template shows how to use the Radial Progress component with different configuration options.
    The report does not use any data; for each component a static value is set for the property "value".
  1. Sparkline

    This report template shows how to use the Sparkline component with different configuration options.
    The report does not use any data; for each component a sub-dataset is feed with static JSON data by using an expression.
  1. LeafLet Markers

    This report template shows how to use the LeafLet Markers component. Marker locations are read by a JSON file provided with the sample.
    Differently from the other samples, this one uses the property saveAsPng since the JavaScript code does not produce an SVG, but a more complex HTML visualization.


Running the Sample

Running the sample requires the Apache Ant library. Make sure that ant is already installed on your system (version 1.5 or later).
In a command prompt/terminal window set the current folder to demo/samples/customvisualization within the JasperReports source project and run the > ant test view command.
It will generate all supported document types containing the sample report in the demo/samples/customvisualization/build/reports directory.
Then the report will open in the JasperReports internal viewer.



© 2001- Cloud Software Group, Inc. www.jaspersoft.com