I’m trying to build multiple reports on Pentaho Report Designer, and couldn’t find a really good way to implement templating. You see – I want the report design to change when I change my template.
So, I wrote some code. The WizardProcessor class is a Pentaho class to be used by report pre processors (see here for more info on the subject), and extending it is easy.
public class TonaWizardProcessor extends WizardProcessor { public MasterReport performPreProcessing(MasterReport definition, DefaultFlowController flowController) throws ReportProcessingException { // Init header ReportHeader header = definition.getReportHeader(); float pageWidth = definition.getPageDefinition().getWidth(); // Add black background Element e = RectangleElementFactory.createFilledRectangle(0, 0, pageWidth, 100, Color.black); header.addElement(e); // Add report title // Add date Element dateElement = DateFieldElementFactory.createDateElement( "TopDateLabel", new Rectangle2D.Double(pageWidth - 100, 0, 100, 100), Color.WHITE, ElementAlignment.RIGHT, new FontDefinition("Arial", 12), "-", "MMM dd, yyyy", "report.date"); header.addElement(dateElement); // Init footer ReportFooter footer = definition.getReportFooter(); // Add date Element footerDateElement = DateFieldElementFactory.createDateElement( "FooterDateLabel", new Rectangle2D.Double(0, 0, pageWidth / 2, 14), Color.BLACK, ElementAlignment.RIGHT, new FontDefinition("Arial", 12), "-", "MMM dd, yyyy", "report.date"); makeBorder(footerDateElement); footer.addElement(footerDateElement); // Add pages count Element footerPagesElement = TextFieldElementFactory .createStringElement("FooterPageLabel", new Rectangle2D.Double( pageWidth / 2, 0, pageWidth / 2, 14), Color.BLACK, ElementAlignment.RIGHT, new FontDefinition("Arial", 12), "-", "PageFunction0"); makeBorder(footerPagesElement); footer.addElement(footerPagesElement); // Create orange row banding Element[] items = definition.getItemBand().getElementArray(); // Setting temp names for the row banding for (Element item : items) { item.setName("TempRowBand"); makeBorder(item); } RowBandingFunction bandingFunction = new RowBandingFunction(); bandingFunction.setVisibleBackground(Color.ORANGE); bandingFunction.setElement("TempRowBand"); definition.addExpression(bandingFunction); // Change all group header background to orange for (int i = 0; i < definition.getGroupCount(); ++i) { for (Element groupHeaderElement : definition.getGroup(i).getHeader().getElementArray()) { makeBorder(groupHeaderElement); groupHeaderElement.getStyle().setStyleProperty( ElementStyleKeys.BACKGROUND_COLOR, Color.ORANGE); } } return super.performPreProcessing(definition, flowController); } private void makeBorder(Element element) { element.getStyle().setStyleProperty(ElementStyleKeys.BOX_SIZING, BoxSizing.BORDER_BOX); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_TOP_WIDTH, new Float(1)); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_LEFT_WIDTH, new Float(1)); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_WIDTH, new Float(1)); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_WIDTH, new Float(1)); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BREAK_WIDTH, new Float(1)); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_TOP_COLOR, Color.black); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_LEFT_COLOR, Color.black); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_COLOR, Color.black); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_COLOR, Color.black); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BREAK_COLOR, Color.black); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_TOP_STYLE, BorderStyle.SOLID); element.getStyle().setStyleProperty(ElementStyleKeys.BORDER_LEFT_STYLE, BorderStyle.SOLID); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_STYLE, BorderStyle.SOLID); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_STYLE, BorderStyle.SOLID); element.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BREAK_STYLE, BorderStyle.SOLID); } }
After you write your class, write a simple JUnit class to test it:
public class ProcessorTest { @Test public void test() { ClassicEngineBoot.getInstance().start(); ResourceManager manager = new ResourceManager(); manager.registerDefaults(); String reportPath = "file:/home/liran/test.prpt"; try { Resource res = manager.createDirectly(new URL(reportPath), MasterReport.class); MasterReport report = (MasterReport) res.getResource(); report.addPreProcessor(new TonaWizardProcessor()); File file = new File("/home/liran/output.html"); PrintStream ps = new PrintStream(file); HtmlReportUtil.createStreamHTML(report, ps); } catch (Exception e) { e.printStackTrace(); } } }
Implementing it at the report level is a bit more difficult, as I couldn't do it in the PRD (Pentaho Report Designer). But changing the layout.xml file (found inside your prpt file) is easy. Just add the following lines:
And you're done.
Note that your pre-processor class (contained in a JAR file) must be found in the REPORT_DESIGNER/lib
directory – otherwise the designer won’t open your report. Same is true for the BI-SERVER (put it in biserver-ee/tomcat/lib
directory).
I’m writing a Java code that will allow you to automatically patch the rprt file to use the pre-processor. I’ll post the code in a future post.