Mark Lorenz on Technology

Wednesday, March 01, 2006

More BIRT Tips for Java Developers

Last month, I wrote some tips for using BIRT from Eclipse to do your report generation. I promised more BIRT tips, so here they are:
  • Control page breaks
Thanks to Jason Weathersby for this tip.
For example, you may want to only have a page break after the first page (or last or whatever conditions). To do this you would:
  • Select the report page and go to the Script page. Add "firstPage = 0;" on a separate line.
  • Add a table to your report.
  • Add a group to your table.
  • Select the group header row and go to the Script page.
  • Go to the onCreate scripting page and add this logic:
          if( firstPage > 0 ) {
this.getStyle().pageBreakBefore = "Always";
} else {
this.getStyle().pageBreakBefore = "Avoid";
firstPage = firstPage + 1;
}
That's it! Now all pages after the first will have a page break before them.

Note: This does not work with a List. Believe me, I tried. So unless you just want the learning experience of a hard spanking, stick to Tables.

Note: To force page "breaks" for HTML output, use the Page Break Interval field. E.g. you could specify a new page every 50 lines. This is necessary because browsers can be resized, so the number of lines can change.

  • Set up your Master page properly
Thanks to Diana Peh for this tip.
The Master page of your report has some rules that must be followed:
  • You can insert only one top-level element in the header or footer. By default
    there's already a date element in the footer. You must delete the date element if you want to add something else (e.g. a page element).
  • If you want to insert multiple elements, delete the date element first,
    insert a grid, then insert as many elements as you want in the grid.
  • No data set fields can be used on the Master page.

  • Avoid specify dimensions in percentages (%)
Percentages for width or height does not work as you might expect. E.g. resizing a browser window will affect the artwork or chart that is sized by percentages. Use inches or some other metric.

A small Report framework to reuse

This isn't exactly a tip, but more of a leg up. Here's an abstract Report class that you can set up a small framework to support BIRT reporting:


/**
*
*/
package com.yourcompany.domain.reporting;

import java.security.InvalidParameterException;
import java.util.HashMap;

import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.EngineConstants;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.HTMLCompleteImageHandler;
import org.eclipse.birt.report.engine.api.HTMLEmitterConfig;
import org.eclipse.birt.report.engine.api.HTMLRenderContext;
import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.eclipse.birt.report.engine.api.ReportEngine;

/**
* @author lorenzm A formatted output document that can be viewed, printed,
* faxed, stored.
* @version $Revision: 1.1 $
*/
public abstract class Report {

/**
* A descriptive String set by my concrete subclasses at construction time.
*/
protected String name;
protected String format =PDF_FORMAT; //default PDF
public static final String PDF_FORMAT =HTMLRenderOption.OUTPUT_FORMAT_PDF;
public static final String HTML_FORMAT =HTMLRenderOption.OUTPUT_FORMAT_HTML;
protected static EngineConfig config;
protected static ReportEngine engine; //only create ONE engine for ALL reports

/**
* Constructor with required associations and/or state
*
* @pre name != null && name.length >0
* @param name
*/
public Report(String name) {
super();
this.name = name;
}

/**
* Set up for BIRT report generation. Subclasses will
* do the actual generation based on their own designs,
* but using the one and only engine I create.
* @return Returns the engine.
*/
protected ReportEngine getEngine() {
if( engine == null ) {
config = new EngineConfig( );
config.setEngineHome( "C:\\BIRT\\birt-runtime-2_0_0\\Report Engine" ); //TODO Set CORRECT home!!
// config.setLogConfig("C:\\BIRT\\logs\\", Level.FINER);
//only create ONE engine for ALL reports
engine = new ReportEngine( config );
// Create the emitter configuration.
HTMLEmitterConfig hc = new HTMLEmitterConfig( );
// Use the "HTML complete" image handler to write the files to disk.
HTMLCompleteImageHandler imageHandler = new HTMLCompleteImageHandler( );
hc.setImageHandler( imageHandler );
// Associate the configuration with the HTML output format.
config.setEmitterConfiguration( HTML_FORMAT, hc );
}
return engine;
}

/**
* Create a formatted output file using a BIRT .rptdesign XML file.
*/
public void generate() {
//------ Run reports
//--> IF params are needed for the report,
// add logic at http://eclipse.org/birt/index.php?page=deploy/engine.html

// Output can be HTML or PDF. PDF is default.
IReportRunnable report = null;
try {
System.out.println(getDesignFilepath()); //TODO remove for production
report = getEngine().openReportDesign( getDesignFilepath() );
} catch (EngineException e) {
e.printStackTrace();
return;
}
// Create a separate task for EACH report
IRunAndRenderTask task = getEngine().createRunAndRenderTask( report );
// Set up options.
HTMLRenderOption options = new HTMLRenderOption( );
// HTML (if specified) or PDF (default)
options.setOutputFormat( format );
String outputPath = "C:\\BIRT\\"; //TODO change to deployment location
outputPath += getOutputFilename();
options.setOutputFileName(outputPath);
task.setRenderOption( options );
HTMLRenderContext renderContext = new HTMLRenderContext();
renderContext.setImageDirectory( "./images" );
HashMap appContext = new HashMap();
appContext.put( EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT, renderContext );
task.setAppContext( appContext );
// Set parameter values using a HashMap. Parameters are name/value pairs.
// The values must be Java objects of the correct type.
HashMap params = getParameters(); //each report has its own params
task.setParameterValues( params );
task.validateParameters();

// Run the report.
try {
task.run( );
} catch (EngineException e) {
e.printStackTrace();
}
}

/**
* Only destroy the BIRT engine when exiting.
*/
public static void shutdown() {
engine.destroy( ); //cleanup
}

/**
* @return Parameter values to use
* Key = param name, Value = param value
*/
protected abstract HashMap getParameters();

/**
* @post $result != null
* @return The location of my BIRT .rptdesign file.
*/
protected abstract String getDesignFilepath();

/**
* @pre format == HTML_FORMAT || format == PDF_FORMAT
* @param format
* Must be HTML or PDF BIRT constant!
* @throws InvalidParameterException
* @throws Exception
*/
public void setFormat(String format) throws InvalidParameterException {
if( format.equals( PDF_FORMAT ) || format.equals( HTML_FORMAT ) ) {
this.format = format;
} else {
throw new InvalidParameterException("Invalid report format: " + format );
}
}
/**
* Return my name as the filename, with ".pdf" or ".html" as the extension
* based on my current format.
*
* @return String my name as the filename
*/
public String getOutputFilename() {
if( format.equals( PDF_FORMAT ) ) {
return getName() + ".pdf";
} else {
return getName() + ".html";
}
}

/**
* @return String
*/
public String getName() {
return name;
}

/**
* Method toString.
* @return String
*/
public String toString() {
return getName();
}
}


A concrete subclass would look like this:


package com.yourcompany.domain.reporting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* A Report of ...
* @author lorenzm
* @version $Revision: 1.1 $
*/
public class MyReport extends Report {
protected One one;
protected Two two;

/**
* Constructor with required parameters.
* @param one ...
* @param two ...
*/
public MyReport(One one, Two two) {
super("My Report");
this.one= one;
this.two= two;
}
/**
* Method getDesignFilepath.
* @return the complete path to my BIRT design file
*/
protected String getDesignFilepath() {
return "C:\\BIRT\\MyReport.rptdesign";
}
/**
* Method getParameters.
* @return my BIRT design input params
*/
protected HashMap getParameters() {
HashMap params = new HashMap();
params.put("one", one);
params.put("two", two);
return params;
}
/**
* Method toString.
* @return String
*/
public String toString() {
return "meaningful string, such as getName() + getParamString()"
;
}
}


That's it for now! Hope this has been beneficial.
Mark

Labels:


3 Comments:

  • Mark,

    Just wanted to get back to you and let you know I had a look at your site. Looks great, I wish more people would start blogging about how things are going with BIRT.

    I went ahead and added a link to your site from BIRT World.

    As you get some more posts, I will talk to Jason and we should be able to get a link to your blog added to the Eclipse Home.

    Thanks for sticking with BIRT.

    Oh, one other thing. I am about to start a little test to check out the viability of trying to use community bookmarks to categorize and make sense of the BIRT Newsgroup, web site, bugzilla, and Wiki entries. It will probably be a little rough to start, we just are working with a rough proto type.

    If you are interested in trying this out, I would love to have you help us prototype the tool. Let me know at (scottr dot innoventsolutions doc com)

    By Blogger Scott Rosenbaum, at April 01, 2006 5:19 PM  

  • Hi Mark,

    Nice Blog! I was wondering if you know a way of adding a group to a table dynamically i.e using the DE API and saving the report.Theres isnt any ocumentation available in this regard.

    Thanks.

    By Blogger Meraj, at January 24, 2007 12:21 PM  

  • Upgrade to Birt 2.1.1 to avail these features,even without the code script mentioned above
    Refer :
    http://www.eclipse.org/birt/phoenix/project/notable2.1.php

    By Blogger suneesh, at February 13, 2007 6:57 AM  

Post a Comment

Links to this post:

Create a Link

<< Home