Out of the box, BIRT gives you several output formats. Because BIRT was designed to be an online reporting tool its default output format is Hypertext Markup Language (HTML). However, you can generate Portable Document Format (PDF) files, and Microsoft® Office formatted Word and Excel® documents, and there are OpenDocument Format emitters in the source tree.
Yet, you may want to generate report output in a format that is not supported by BIRT out of the box. For example, you may want to generate raw Extensible Markup Language (XML) representations of report output that you can process further using Extensible Stylesheet Language Transformation (XSLT) or use for easy processing in unit testing. And by knowing how to work with the emitter extension point, you can enhance the existing emitters. For example, you can add security or encryption to the PDF output format or image support to the existing open source Excel output. This article guides you through creating some basic BIRT emitters using two different approaches in the emitter framework.
The BIRT emitter extension point
Emitters in BIRT are Eclipse extension point plug-ins. Emitters are called at render time only, after all report data is retrieved and the temporary RPTDOCUMENT file is created. As Figure 1 illustrates, this allows you to execute a report once and render to multiple formats without requiring the expensive process of retrieving data and building the RPTDOCUMENT.
Figure 1. BIRT emitters allow you to execute a report once and render to multiple formats
There are two primary interfaces defined for creating report output:
ContentEmitter and
PageEmitter. The most commonly used interface
for writing BIRT emitters is ContentEmitter,
which uses an observer pattern similar to the one Simple API for XML (SAX)
uses. In the case of BIRT’s emitter framework, the developer is only
responsible for creating the observer. So a
<report> tag is encountered from the
render engine, and there is a corresponding
startReport() method to handle that event. You
also define an endReport() method to handle
</report> tags. This is true for all BIRT
report item types (grids, tables, rows, cells, labels, data, text, and
extended item types), with all having a start method and most having an
end method. There are some exceptions, which the example in this article
illustrates. Fortunately, the interfaces already define these methods to
override.
The ContentEmitter interface works great for
structured output such as HTML. But when you need pixel-perfect output,
you use the layout PageEmitter extension, which
is how the PDF, Excel, and third-party Tribix emitters work. The basic
idea is that there is an IPageDevice object
that gets created at render time by the emitter framework, and that
IPageDevice is responsible for creating
IPages that are responsible for drawing the
output using an X and Y coordinate system.
IPages are observers, whereas the
IPageDevice is a factory for
IPages. There are a couple other factory
objects that you need to create that the example for using the
PageEmitter covers.
XML example using the content emitter
This example guides you through creating a basic XML output emitter. The XML output basically just outputs the element type encountered and the values inside. This is useful if you have some sort of XML processor that handles output, want to do some sort of XSLT transformation, or want to unit test report designs.
To create a basic XML output emitter, you need to do the following:
- Create a plug-in project.
- Define the emitters the plug-in provides.
- Test the new plug-in.
The first step in creating a basic XML output emitter is to create a plug-in project.
- In Eclipse, select File>New>Other (Figure 2).
Figure 2. Eclipse File menu
- In the Wizards selection window, select
Plug-Development>Plug-In Project (Figure 3).
Figure 3. Wizards selection window
- Give the project a name in the New Plug-in Project window (Figure 4).
Figure 4. Plug-in Project window
- In the following window (Figure 5), you set up
some basic metadata about the plug-in project. Clear the This
plug-in will make contributions to the UI check box
because the emitter will not make any user interface (UI) changes and
will not require any Eclipse UI components.
Figure 5. Plug-in Project Content window
- In the Templates selection window (Figure 6),
clear the Create a plug-in using one of the templates
check box.
Figure 6. Templates selection window
- Click Finish.
- The first screen that displays for a plug-in project is the Manifest
editor (Figure 7). You need to set a few things in
this screen to begin developing the emitter.
Figure 7. Manifest editor
- First, click the Extensions tab and click Add (Figure 8).
Figure 8. Extension tab
- In the Extension Point Selection window (Figure
9), use the
org.eclipse.birt.report.engine.emittersfilter. Be sure to clear the Show only extension points from the required plug-ins check box.
Figure 9. Extension Point Selection window
- When asked if you want to add the dependency for
org.eclipse.birt.report.engine(Figure 10), click Yes.
Figure 10. New plug-in dependency dialog box
Defining the emitters the plug-in provides
Now that you have a new plug-in project, you can define the emitters that this plug-in provides. An emitter plug-in project can provide multiple emitter output types. For example, a single image emitter can provide output to JPEG, Portable Network Graphics (PNG), Graphics Interchange Format (GIF), and bitmap formats.
- In the Extensions tab (Figure 11), click on the
newly added emitter and fill in the appropriate metadata. Make sure
you note the class name you use in this section because that is the
class that handles all the emitter events. Also, whatever you enter
for the format is what displays for the type of output to generate in
any drop-down menu in the BIRT Report Designer or Sample BIRT Report
Viewer. Be sure to set the ID field to something unique. In this
example, I use the same name as the class. You can leave the default
values for all the other fields.
Figure 11. Extensions tab with new emitter
- Create a new Java™ class with the package and name matching
what you entered in the Manifest editor (Figure
12).
Figure 12. New Java Class window
This class should implement theorg.eclipse.birt.report.engine.emitter.IContentEmitterinterface (Figure 13).
Figure 13. Implement Interfaces Selection dialog box
- Use the code in the com.digiassn.blogspot.birt.emitter.XmlEmitter.java file as the implementation code (see Download).
Now that you've created the code for the emitter, you need to launch a test instance of Eclipse to make sure that everything is working.
- Open the Manifest.MF file and click the Launch an Eclipse
Application link (Figure 14).
Figure 14. Overview tab
- If you go to Run>View Report in the instance of
Eclipse that is running, a new entry called As XML is
available (Figure 15).
Figure 15. New As XML entry on the Run>View Report menu
- Using a simple report that has a single dynamic text element with 2 +
2 as the expression, the output looks like Figure
16.
Figure 16. Example output
Working with the layout page emitter
What if your desired output isn't structured like XML but uses a
free-floating layout? Maybe you want something a little more pixel
perfect, such as an image canvas or HTML using Cascading Style Sheets
(CSS) for coordinates. The structured emitter won't work. The good news is
that BIRT's emitter endpoint has classes for handling this situation under
the org.eclipse.birt.report.layout.emitter
package. The bad news is that working with this framework is more
difficult because you need to implement several different classes.
The following example walks you through the classes you need to create to
implement a PageEmitter-based plug-in for BIRT.
This plug-in outputs a rendered JPEG image of the report output. It uses
Abstract Window Toolkit (AWT) to create a canvas and draw the output from
BIRT. AWT makes it easier to work with the colors and coordinate system
passed in from BIRT. However, some of the techniques I use in this example
are expensive in terms of time and memory.
You need to use a pagination setting that tells BIRT to output pages,
whether it is page break or page size. The first class you need to
implement is the PageEmitter (Listing 1). Unlike most classes in the emitter framework, this is
actually an extended class rather than an implementation of an interface.
This is a simple class with a simple responsibility. It is the class that
you set in the Manifest as the implementation class for the emitter
extension. It is only responsible for creating a
PageDeviceRender class using its only available
method, createRender(IEmitterServices). You can
handle any configuration options for the output using the
IEmitterServices class.
Listing 1. The
PageEmitter class
package com.digiassn.blogspot.birt.layout.emitter;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.emitter.IEmitterServices;
import org.eclipse.birt.report.engine.layout.emitter.PageDeviceRender;
import org.eclipse.birt.report.engine.layout.emitter.PageEmitter;
public class JpegPageEmitter extends PageEmitter {
@Override
public PageDeviceRender createRender(IEmitterServices services)
throws EngineException {
return new JpegPageDeviceRender( services );
}
}
|
The next class you create is the
PageDeviceRender (Listing
2). It too has simple responsibilities. It creates an
IPageDevice when called. This class is actually
an implementation of an IAreaVisitor, but you
don't need to work with the IAreaVisitor to
implement an emitter. It is much easier to work with extending the
PageDeviceRender.
Listing 2. The
PageDeviceRender class
package com.digiassn.blogspot.birt.layout.emitter;
import java.io.OutputStream;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.script.IReportContext;
import org.eclipse.birt.report.engine.content.IReportContent;
import org.eclipse.birt.report.engine.emitter.EmitterUtil;
import org.eclipse.birt.report.engine.emitter.IEmitterServices;
import org.eclipse.birt.report.engine.layout.emitter.IPageDevice;
import org.eclipse.birt.report.engine.layout.emitter.PageDeviceRender;
public class JpegPageDeviceRender extends PageDeviceRender {
private OutputStream output;
/**
* Setup basic emitter services used throughout
* @param services
*/
public JpegPageDeviceRender(IEmitterServices services) {
try {
this.output = EmitterUtil.getOuputStream( services, "report.jpg" );
} catch (EngineException e) {
e.printStackTrace();
}
}
/**
* Called by BIRT to create a page device.
*/
@Override
public IPageDevice createPageDevice(String title, String author, String subject,
String comments, IReportContext reportContext, IReportContent report)
throws Exception {
this.pageDevice = new JpegLayoutPageDevice(output);
return this.pageDevice;
}
/**
* Used to get the output format. This should match what is in the Manifest and Plugin.XML
*/
@Override
public String getOutputFormat() {
return "jpg";
}
}
|
The next class that you create is the
IPageDevice (Listing 3).
This class is called whenever a new page needs to be generated in BIRT
report output. This class handles whatever the overall output format is
and manages the individual pages to be added into the final output. In the
JPEG emitter created here, there will only be one page handled, so it only
needs to create a single ImageBuffer and
Graphics object. You could employ other
strategies, such as writing multiple images to an output directory and
zipping all of them or appending to the end of an image file.
Listing 3. The
IPageDevice class
package com.digiassn.blogspot.birt.layout.emitter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import org.eclipse.birt.report.engine.layout.emitter.IPage;
import org.eclipse.birt.report.engine.layout.emitter.IPageDevice;
/**
* This class is responsible for creating and managing pages during render.
* Since this is a simple example, this class will only handle a single page.
*
*/
public class JpegLayoutPageDevice implements IPageDevice {
//the AWT canvas used to draw the image
private Graphics2D canvas;
//output stream to write to
private OutputStream output;
//buffered image that will be written to the output
private BufferedImage bufferedImage;
//set the scale of the image.
double scale = 0.01;
/**
* Constructor just initializes the output stream read from the
* render options
* @param output
*/
public JpegLayoutPageDevice(OutputStream output) {
this.output = output;
}
/**
* Write file on close.
*/
@Override
public void close() throws Exception {
//use ImageIo to write out the file
ImageIO.write(bufferedImage, "jpg", output);
}
/**
* Utility method to calculate the scale size.
* @param size
* @return
*/
private int scale(int size)
{
return Math.round((float)(size * scale));
}
/**
* Called by BIRT when a new page needs to be created
*/
@Override
public IPage newPage(int width, int height, Color color) {
//get the scaled width and height, and create an
//image buffer based on that size
int scaledWidth = scale(width);
int scaledHeight = scale(height);
bufferedImage = new BufferedImage(scaledWidth,
scaledHeight,
BufferedImage.TYPE_INT_RGB);
//get the canvas to draw on from the buffered image
canvas = bufferedImage.createGraphics();
//check the background color. If one is not set, default
//to white
if (color == null)
{
canvas.setBackground(Color.WHITE);
}
else
{
canvas.setBackground(color);
}
//clear background color
canvas.clearRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
//create a new page device
return new JpegPage(canvas, scale);
}
}
|
And the final class that you need to implement is the
IPage (see the
com.digiassn.blogspot.birt.layout.emitter.JpegPage.java file in the Download). This class is responsible for handling
the drawing events, similar to how the
ContentEmitter works. The exception is that
there are only a handful of events, such as
drawLine and
drawText. Outside of some styling information,
coordinates, and the final evaluated values to output, nothing else is
provided.
Figure 17 is the output from the JPEG image emitter. There are some minor tweaks that you could make, but this should give you a general idea of how it works.
Figure 17. The output image from the JPEG output emitter
This article covered the basic requirements for implementing two different
types of BIRT emitters: ContentEmitter and
PageEmitter. You saw how to create an observer
object to handle events that the ContentEmitter
executes. These events create an XML document containing the values of the
report output, and you can use them for any number of XML processing
routines. You also saw how to create the factory objects used by the
PageEmitter and how to create the observer for
handling page render events. Using AWT you created a simple emitter that
created JPEG images of BIRT reports. You should now have a basic
foundation for creating BIRT emitters.
| Description | Name | Size | Download method |
|---|---|---|---|
| Eclipse plug-in project source for the examples | emitterProjectSource.zip | 19KB | HTTP |
Information about download methods
Learn
- Using Eclipse BIRT extension points (developerWorks, Dec 2009):
See John's previous article to learn more about the aggregation extension
point.
- The
Digital Voice: Check out John's blog.
- BIRT Exchange: Visit this online community focused on
BIRT.
- developerWorks
Business analytics: Find more analytic technical resources for
developers.
-
developerWorks Open
source: Find extensive how-to information, tools, and project
updates to help you develop with open source technologies and use them
with IBM products.
- developerWorks on
Twitter: Join today to follow developerWorks tweets.
- developerWorks podcasts: Listen to interesting interviews and
discussions for software developers.
-
developerWorks technical events and webcasts: Stay current with
developerWorks technical events and webcasts.
Get products and technologies
- BIRT Project: Download BIRT
from eclipse.org.
-
Evaluation
software: Download or explore
the online trials in the IBM SOA Sandbox and get your hands on
application development tools and middleware products from DB2®,
Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- developerWorks
community: Connect with other developerWorks users while exploring
the developer-driven blogs, forums, groups, and wikis.
-
developerWorks profile: Create your profile today and set up a watchlist.
John Ward is an independent consultant, specializing in BIRT, GWT, and e-commerce search and navigation solutions. John worked several years as a consultant for Innovent Solutions developing solutions based on BIRT. Prior to that, John was an assistant vice president for Citibank North America, managing the training MIS group and overseeing developing of new technology-based training initiatives. He actively works with and tests BIRT, including development work based on BIRT reports and the BIRT APIs. He is the author of Practical Data Analysis and Reporting with BIRT and maintains The Digital Voice blog.



