Introduction to Custom Components in iReport Designer


This is an article based on work one of our Professional Services consultants recently completed. In this article, he discusses how to create custom components in iReport, and enabling their use in iReport's palette.

This article is meant for developers who are interested in developing a custom component for the iReport tool. The article uses the NetBeans IDE to develop the example. It contains code samples in Java, and makes reference to configuration files.

You can find the Word version of this article here: Creating Custom Components in iReport.

Problem to Solve

A client requests to add a new item into the Palette window of iReport in order to easily let a report designer insert a QRCode, which is a type of barcode, into their report. The client is using Jasperserver 3.7 and iReport 3.7.6. The client also expects to be able to encode QR Code using different character set.

JasperReports API Version

Let's take some time to summarize which environment is the best accordingly to your needs. Depending on the environment you are aiming to deploy your component for, it is important to develop against the right version of JasperReports API. For example, if you plan to deploy a report which will contain your component into JasperServer 3.7, you must use JasperReports v3.7.1.1 library since Jasperserver 3.7 support only JR v3.7.1.1 and earlier. But if the report will be produce only on iReport v3.7.6 and will never be deployed on Jasperser then you can use JR 3.7.6.

As shown in the table below it is important to choose the right version of JasperReports library to make sure your custom component will be compliant with the desired environment.

API Version
v4.0 X X X X X
3.7.6   X X   X
4.0     X   X

QR Code

Here is the QR Code definition according to Wikipedia:

A QR Code is a specific matrix barcode (or two-dimensional code), readable by dedicated QR Barcode reader and camera phones. The code consists of black modules arranged in a square pattern on a white background. The information encoded can be text, URL or other data.


iReport is a Java-based program that helps users and developers that use the JasperReports library to visually design reports. Through a rich and very simple to use GUI, it provides all the most important functions to rapidly create nice but very complex reports, saving a lot of time.

Palette Window

Most of the items that can be integrated into a report are displayed in the palette window. To open this window, shown in Figure 2, select Window -> Palette (Ctrl + Shift + 8).

Implementation steps

To implement this requirement we need to:

  • Create the custom component that will provide the QR Code functionality
  • Create the Plugin to integrate the custom component into iReport.

It is highly recommended to use NetBeans IDE when creating plugin since it provides the developer with all the required plug-in development facilities. Based on this recommendation, this tutorial is using NetBeans technology.

Creation of the custom component

Since the client is looking for a solution compliant with iReport version 3.7.6, the JasperReports 3.7.6 library will be used.

The following section defines the custom component, as described in JasperReports Ultimate Guide.


A custom component is a feature that allows users to extend the functionality of the JasperReports engine by plugging-in custom-made visual components that take data as input and produce content that is embedded into the resulting documents. Custom components can be anything from simple barcodes to tables, and professional charting packages.

The custom component support relies on two other major features of the JasperReports library: extension support and generic elements. Both are described in separate sections of JasperReports ultimate Guide and you need to be familiar with them before delving into custom components.

A custom component is a special report element that has a defined structure and produces some output in generated reports, just like usual report elements do. Just like a normal chart element in a report template will produce an image element in the generated document, or a crosstab element present in the report template will produce a series of frames and text fields that makes up a tabular structure in the generated documents, generic custom components declared in a report template could generate any combination of simple elements and generic elements as the output of their built-in calculation and layout logic.

Custom components come in bundles. A component bundle is a package comprising of one or several components that share the same XML namespace and schema. For example, a chart component bundle can contain multiple chart components, one for each type of chart (pie chart component, bar chart component and so on). Now that we all understand what a custom component is, let's begin our implementation.

Implementation overview

We can define four (4) milestones that need to be taken care of to implement a custom component.

  1. Define the interface of the custom component
  2. Transformation XML <- -> Java Object
    1. Implementation of the report lifecycle
    2. Compilation
  3. Filling
  4. Glue all the pieces together

The following section will detail those steps using the QR Code sample.


We need to design the XML schema use for the custom component element. The schema defines the target namespace and the definition of the QR Code. It can define as many elements as you want, for the sake of the demonstration only one element is define. The figure below represents the file component.xsd that defines the schema of the QR Code element. Let's describe the content of the file.

  1. The target namespace. Must be unique per schema.
  2. Indicate the usage of "jr" prefix to define namespace
  3. Name of our QR Code element
  4. Indicate that element can substitute a "jr:component"
  5. Indicate that element extends "jr:componentType"
  6. Indicate the use of "code expression" that will provide the text that need to be encoded as a QR Code
  7. List of attributes for our element

Once the component interface has been defined and the XML schema implemented, we need a Java object model that mirrors the schema.

This model is composed of an Interface called: QRCodeComponent that extends net.sf.jasperreports.engine.component.Component interface and contains the fields that match the QRCode XML element properties. A default implementation is provided with StandardQRCodeComponent:

public class StandardQRCodeComponent implements QRCodeComponent, Serializable, JRChangeEventsSupport
    private String charSet;
    private String errorCorrection;
    private JRExpression codeExpression;
    private EvaluationTimeEnum evaluationTimeValue = EvaluationTimeEnum.NOW;
    private String evaluationGroup;


We need to take care of the transformations of XML fragments to Java Objects and back. In other words, we need to implement the code that will generate the XML into the "jrxml" file while creating a report and digest the XML at runtime into our Java object (QRCodeComponent). Parsing an XML fragment into a corresponding Java object is done by writing a class that defines Commons Digester rules that apply to the QRCode XML element. We use the class for this operation, which implements XmlDigesterConfigurer and ComponentXmlWriter. "ƒ

Java Object -> XML

Producing XML fragments from a QRCodeComponent object is the task of the writeToXml method of the same class. The method receives a component instance and an XML report writer and outputs to the writer the XML fragment that corresponds to the component.

public void writeToXml( ComponentKey componentKey, Component component,
                                      JRXmlWriter reportWriter) throws IOException
    if (component instanceof QRCodeComponent)
        QRCodeComponent barcode = (QRCodeComponent) component;
        writeQRCode(barcode, componentKey, reportWriter);
protected void writeQRCode( QRCodeComponent barcode, ComponentKey componentKey,
                                               JRXmlWriter reportWriter) throws IOException
    JRXmlWriteHelper writer = reportWriter.getXmlWriteHelper();
    XmlNamespace namespace = new XmlNamespace( ComponentsExtensionsRegistryFactory.NAMESPACE,
                                               ComponentsExtensionsRegistryFactory.XSD_LOCATION );
    writer.startElement("qrcode", namespace);
    writer.addAttribute("charSet", barcode.getCharSet());
    writer.addAttribute("errorCorrection", barcode.getErrorCorrection());
    if (barcode.getEvaluationTimeValue() != EvaluationTimeEnum.NOW)
    barcode.getCodeExpression(), false);

XML -> Java Object

Digesting the XML schema into a Java object is performed into configureDigester method.

The class registers rules as the following:

public void configureDigester(Digester digester)
    log.debug("Rules configuration");
    String barcodePattern = "*/componentElement/qrcode";
    digester.addObjectCreate( barcodePattern, StandardQRCodeComponent.class );
    digester.addSetProperties( barcodePattern,
                               //properties to be ignored by this rule
                               new String[]{ JRXmlConstants.ATTRIBUTE_evaluationTime },
                               new String[0] );
    digester.addRule( barcodePattern,
                      new XmlConstantPropertyRule( JRXmlConstants.ATTRIBUTE_evaluationTime,
                                                   EvaluationTimeEnum.values() )
    String barcodeExpressionPattern = barcodePattern + "/codeExpression";
    digester.addFactoryCreate( barcodeExpressionPattern,
                               JRExpressionFactory.StringExpressionFactory.class.getName() );
    digester.addCallMethod( barcodeExpressionPattern, "setText", 0 );
    digester.addSetNext( barcodeExpressionPattern,
                         JRExpression.class.getName() );

Report life cycle


We will now write the code that manages qrcode components during report compilation. At report compilation time, custom components need to be validated, expressions need to be collected from them and component instances have to be generated for inclusion in compiled reports.

The QRCodeCompiler class contains three methods that take care of these tasks:

  1. The verify method checks that the QRCode expression and type have been specified (and logs broken rule errors if the component is not valid)
public void verify(Component component, JRVerifier verifier)
    QRCodeComponent barcode = (QRCodeComponent) component;
    JRExpression codeExpression = barcode.getCodeExpression();
    if (codeExpression == null)
        verifier.addBrokenRule("QR Code expression is null", barcode);
        String valueClass = codeExpression.getValueClassName();
        if (valueClass == null)
            verifier.addBrokenRule("QR Code expression value class not set", codeExpression);
        else if (!"java.lang.String".equals(valueClass))
            verifier.addBrokenRule( "Class " + valueClass 
                                    + " not supported for barcode expression. Use java.lang.String instead.",
                                    codeExpression );
    EvaluationTimeEnum evaluationTime = barcode.getEvaluationTimeValue();
    if (evaluationTime == EvaluationTimeEnum.AUTO)
        verifier.addBrokenRule("Auto evaluation time is not supported for barcodes", barcode);
    else if (evaluationTime == EvaluationTimeEnum.GROUP)
        String evaluationGroup = barcode.getEvaluationGroup();
        if (evaluationGroup == null || evaluationGroup.length() == 0)
            verifier.addBrokenRule("No evaluation group set for barcode", barcode);
        else if (!verifier.getReportDesign().getGroupsMap().containsKey(evaluationGroup))
            verifier.addBrokenRule("Barcode evalution group \"" + evaluationGroup + " not found"
  1. The collectExpressions method collects the expression embedded into the component
public void collectExpressions(Component component,
                                    JRExpressionCollector collector)
    QRCodeComponent barcode = (QRCodeComponent) component;
  1. The toCompiledComponent method creates a barcode object which includes the compiled expression instance obtained from net.sf.jasperreports.engine.base.JRBaseObjectFactory (which is an object factory that produces object instances for compiled reports).
public Component toCompiledComponent(Component component,
                                     JRBaseObjectFactory baseFactory)
    QRCodeComponent barcode = (QRCodeComponent) component;
    StandardQRCodeComponent compiledBarcode
        = new StandardQRCodeComponent(barcode, baseFactory);
    return compiledBarcode;

As report compilation has been covered, we go on to the next phase in a report's life cycle, namely report fill.


At filling time, we need to get a QRCode component instance as designed in a report template, collect dynamic data from the report data set and produce an element that will get included in the generated report.

This is the job of a class that implements the net.sf.jasperreports.engine.component.FillComponent interface, and we will name our implementation QRCodeFillComponent. Fill component instances are created by a factory class called QRCodeFillFactory. Since our project contains only one component, the Factory will always return an instance of QRCodeFillComponent.

The QRCodeFillComponent class extends the abstract net.sf.jasperreports.engine.component.BaseFillComponent class that provides basic functionality for component fill implementations. The class includes methods that are called by the engine at different stages during the fill process.

The evaluate method evaluates the qrcode expression and stores the result in a class member (code) which is going to be used later in the process:

public void evaluate(byte evaluation) throws JRException
    code = (String) fillContext.evaluate( barcodeComponent.getCodeExpression(),
                                          evaluation );

The prepare method allows the fill component to decide whether it will produce an output element, and whether it needs to stretch the space assigned at design time for the element in order to fit the generated output. The fill barcode implementation checks whether the barcode expression has yielded a non-null result and specifies in this case that it will produce a print element that does not stretch; otherwise it specifies that it will not produce an output element:

public FillPrepareResult prepare(int availableHeight)
    return code == null ? FillPrepareResult.NO_PRINT_NO_OVERFLOW
                        : FillPrepareResult.PRINT_NO_STRETCH ;

Before implementing the fill method which will actually produce the report element that will get included in the filled report, we get to the point where we need to come up with what we want to include in the generated report. QRCodes can be rendered as images, so writing an image renderer that knows how to draw a QRCode would do the job. Thus we write the QRcodeRenderer class which is a SVG renderer that uses the ZXing API to draw a barcode.

Now that we have the QRCode image renderer, we can write the fill method of QRCodeFillComponent which will create images that use this renderer. The method uses the evaluated QRCode expression and the QRCode component attributes to create a barcode renderer:

public JRPrintElement fill()
    JRTemplateImage templateImage = getTemplateImage();
    JRTemplatePrintImage image = new JRTemplatePrintImage(templateImage);
    JRComponentElement element = fillContext.getComponentElement();
    QRCodeInfo barcodeInfo = new QRCodeInfo();
    QRCodeRenderer renderer = new QRCodeRenderer(barcodeInfo);
    return image; 

Glue the pieces together

We have all the code required to handle QRCode components and we need to put it all together into a component package. Components are registered as JasperReports extensions, and we will use a hard coded extension factory for our component. We will write a class that implements net.sf.jasperreports.extensions.ExtensionsRegistryFactory and returns our component implementation as extension. In our implementation, this class is The class creates a component manager object which glues handler classes used for report compilation and filling. Component transformations between JRXML and object model are the responsibility of an XML handler object, which also specifies the XML namespace and schema for the component. To register the component extension, we will need to write a file and put it as a resource at the root of our class folder. The file contains a property that registers our extension factory:



To test our implementation we need to create a sample report that contains our QRCode component.

    <reportElement x="17" y="14" width="50" height="50"/>
    <jr:qrcode xmlns:jr="ttp://"

The QRCode component is wrapped in a componentElement report element that also includes common element attributes such as position and size. The component itself uses the namespace we have chosen for our implementation, and specifies the barcode type and code expression.

To wrap everything up, the classes need to be compiled and packaged into a JAR file together with the rest of the resources used by the code.

Most of the time, if not every time, we would like to use this custom component inside iReport to let the report designer add this component into their design. The following section describes how to create the plugin and deploy it under iReport.

Creation of the plugin in NetBeans


In order to create a plugin that will be added into iReport to use our component we need the following:

NetBeans Project

  • Add iReport NetBeans platform to the environment
    • Tools -> NetBeans Platform -> Add Platform...

  • Choose the iReport platform you would like to develop your plugin on. (Root folder of iReport installation. Platform Name should be filled automatically)
  • Click Next
  • Rename the platform if needed, then Finish. (In this tutorial, the platform name is: Jaspersoft iReport Designer 3.7.6)
  • Create a New Project
    • File -> New Project... -> NetBeans Modules -> Module

  • Click Next
  • Enter the Project Name, Project Location
  • select the "Standalone Platform" radio button
  • Choose Jaspersoft iReport Designer v3.7.6 from the dropdown list, click Next

  • Enter the Code Name Base, the Module Display Name
  • Check "Generate XML Layer", then Finish

The figure below displays the tree created at the end of the wizard.

  • Run your project (with the Rub Main Project button on the tool bar). A special version of iReport will start. Even if our plug-in does nothing yet, you can see the it in the list of installed plugin in the plugin manager (Tools -> Plugins).

Close iReport and come back to the IDE. It's time to write some code.

Implementation of the plugin

Close iReport and come back to the IDE. It's time to write some code.

As a reminder, our client wants to add the component into the Palette window as shown in the figure below.

IRPITEM description file

The first thing we should do is create an irpitem file. Irpitem stands for iReport Palette item. This file is only needed when you desire to add your component into the Palette window.


"" and "qrcode.tooltip" can be translated in a resource Bundle added to the global resource bundle of iReport, in this case an installer class for your module should do something like:


To create a module installer class, use the NetBEans wizard.


To add the palette item in the iReport palette we have to modify the layer.xml file (an important file of your NetBeans plugin). You need to add the following chunk of xml to your layer.xml:

<folder name="palette">
    <folder name="ReportElements">
        <attr name="SystemFileSystem.localizingBundle"
              stringvalue="com.jaspersoft.ireport.locale.Bundle" />
        <file name="QRCodeItem.irpitem" url="QRCode.irpitem" />

This will add your item inside the ReportElements section of the palette. You can even create your own palette section, or use the Tools section.

The Big Picture

Now that our item appears in the Report Elements of the palette window, let's have a look on what happen under the hood when we drag'n'drop this item inside any band of the report.

  1. Create a JRDesignElement that include our Custom Component.
  2. Create a JRDesignElementWidget object that will manage all events that may occur on our JRDesignElement object.
  3. Create the ElementNode object including its properties


As specified in our irpitem file ("action" property in Figure 21) we have to implement our extension of com.jaspersoft.ireport.designer.palette.PaletteItemAction or, as we did in this sample, it's more specific implementation com.jaspersoft.ireport.designer.palette.actions.CreateReportElementAction. overrides "createReportElement" to build our own JRDesignElement and link it to our Custom component.

The following listing represents the code from QCAction that create a JRDesignComponentElement, set default attributes and link to our custom component.

public JRDesignElement createReportElement(JasperDesign jd) {
    JRDesignComponentElement component = new JRDesignComponentElement();
    StandardQRCodeComponent componentImpl = new StandardQRCodeComponent();
    JRDesignExpression exp = new JRDesignExpression();
    component.setComponentKey( new ComponentKey( "", "jr", "qrcode" ) );
    return component;


The responsibility of this factory is to create a JRDesignElementWidget and an ElementNode object based accordingly to the component. In this tutorial, the factory only needs to manage one component, QRCodeComponent.

The JRDesignElementWidget object will be injected into our QRCodeComponent as a PropertyChangeListener and will manage all the events that may occurred on the item's properties inside iReport. Pay attention to the statement beginning with c.getEventSupport() in the code sample below.

For example, when we increase the QRCode item in the report, the engine will check if a JRDesignElementWidget has been injected into the component and then call its propertyChange method.

public class JRQRCodeComponentWidget extends JRDesignElementWidget {
    public JRQRCodeComponentWidget( AbstractReportObjectScene scene, JRDesignElement element ) {
        super(scene, element);
        if (((JRDesignComponentElement)element).getComponent() instanceof StandardQRCodeComponent)
            StandardQRCodeComponent c = ( StandardQRCodeComponent ) ( ( JRDesignComponentElement)element ).getComponent();
    public void propertyChange(PropertyChangeEvent evt) {
        if ( evt.getPropertyName().equals(StandardQRCodeComponent.PROPERTY_CHAR_SET) ||
             evt.getPropertyName().equals(StandardQRCodeComponent.PROPERTY_ERROR_CORRECTION) ||

The ElementNode is as its name says an object that can be added into a band in iReport. As you can see on Figure 23, the QR Code is an element node contained inside "Detail 1" band (node).

At the code level, this Element Node is the wrapper of our component and its properties. Each time our QR Code item is selected, either from the tree or inside the design section of the report, jasperreports engine calls the method createSheet. This method define which properties should be included into the properties window. Refer to the next section titled Component's properties.

protected Sheet createSheet() {
    Sheet sheet = super.createSheet();
    // adding common properties...
    Sheet.Set set = Sheet.createPropertiesSet();
    StandardQRCodeComponent component = (StandardQRCodeComponent)(   (JRDesignComponentElement)getElement()).getComponent();
    JRDesignDataset dataset = ModelUtils.getElementDataset(getElement(), getJasperDesign());
    set.put(new QRCodeCodeExpressionProperty(component, dataset));
    set.put(new QRCodeEvaluationTimeProperty(component, dataset));//, dataset));
    set.put(new QRCodeEvaluationGroupProperty(component, dataset));
    set.put(new QRCodeCharSetProperty(component) );
    set.put(new QRCodeErrorCorrectionProperty(component) );
    ModelUtils.getElementDataset(getElement(), getJasperDesign())));
    sheet.put( set);
    return sheet;

Component's properties

The component's properties are the input parameters (optional or mandatory) that need to be passed to the custom component. Each properties must extend org.openide.nodes.Node.Property or one of its child classes such as StringProperty, ExpressionProperty, PropertySupport or else. As shown in the fiure below, the component supports five (5) specific properties:

  • Code Expression
    • Which is the String that will be encoded as a QR Code
  • Evaluation Time and Evaluation Group
    • Defines the time at which the component has to be processed. The evaluation of an expression can be done when the report engine "encounters" the element during the creation of the report (evaluation time "now") or it can be postponed.
  • Character Set
    • Charset used to encode the code expression
  • Error Correction

Let's focus on the Character set property. A class named: QRCodeCharSetProperty extends The implementation provides the information on the property such as the name and the description (as seen at the bottom of Figure 24), validates the input value (must be a valid Character Set) and store this value for future use.


We are now ready to deploy our hard work to iReport.

Create the NBM file


Note that iReport 3.7.6 has been developed on NetBeans 6.5.1. So if you are using NetBeans 6.9.1, you will have to add the following property


at the end of this property file ($ProjectRoot/nbproject/ in order to disable the pack200 feature. Otherwise, the plugin install in iReport will not work.


  1. Packaging our plugin for deployment, we need to generate a NBM file( NetBeans Module).
    1. From NetBeans IDE, right-click on your plugin project and select "Create NBM".
  2. Installing the plugin into iReport
    1. Launch iReport v3.7.6 or later
    2. Tools -> Plugins
    3. Choose "Downloaded" tab
    4. Click on "Add Plugins..." button
    5. Browse to your NBM file generated in step 1.a, then "Open"
    6. Click on the "Install" button

You are now ready to use your new component.