Alvaro Pedraza
Alvaro Pedraza

Reputation: 1292

How can I build a report with large summary in JasperReports

I'm trying to accomplish some requirements with a report I'm designing for a desktop app. I'll try to explain myself as clearly as it's possible to me. The situation is as follows:

I have a report with a simple table in the <detail> band and then a very big section of some static text and 2 or 3 expressions, which should correspond to the <summary> band. In a first moment, I tried to put all that information in the <summary> but then I found out the 656px height limitation for JasperReports bands.

My second attempt to solve that problem was to put that static summary information in a subreport. That way I was able to use <title> and <summary> bands to put the fields in both bands and don't have to worry about band height limitation. The problem with that solution is that I wasn't able to show page numbers (from the first report) in the subreport section (which has 2 or 3 pages). I found the option of page footer and header in summary (a checkbox in Jaspersoft Studio which is isSummaryWithPageHeaderAndFooter="true" property in <jasperReport> element) but then my report gave me a compilation error on preview; don't know if it's a bug in Jaspersoft Studio: I tried in the 2 latest versions and the error was the same.

Finally, I tried to add a second <detail> band with a different query which returns only a single value. The problem now is that I wasn't able to put the second detail band "below" the first: in the preview I see one row from each band alternately, and not as I need. After searching a lot I found out that this is not possible.

Summary requirements causing problems

The summary has the following requirements:

  1. Has severals static text fields and some other expressions with calculated fields and parameters (formatted dates and things like that).
  2. Those pages must be paginated (page [current] of [total] in the footer).
  3. The datasource and the query is the same for the main report and the summary.

The first requirement makes the summary band to need a height bigger than 656px, which is the max allowed for that band. So I tried the solutions described briefly above. I'll describe now the problems for the subreport approach.

Subreport with isSummaryWithPageHeaderAndFooter="true"

When I try to preview the report from Jaspersoft Studio with this approach, first I get the following state (before the IDE ask for the parameter):

First report state

When I enter the parameter, I get the following state:

Second report state

and after that the times and the pages keep growing until the program crashes. The same behavior happens in different installations: I tried versions 6.3.1 and 6.4.0 in both Windows 7 and Mac OS. HOWEVER, compiling the report from the IDE is successful (I mean it generates the .jasper file) with the compile report option.

compile report option

But when I export the report to PDF (or display it with JasperViewer) it doesn't render with page footer in summary band.

Note: Compiling the report from a simple Java app doesn't gives me any error.

Any help is welcomed. Thanks in advance.

Upvotes: 4

Views: 4518

Answers (2)

Alvaro Pedraza
Alvaro Pedraza

Reputation: 1292

After a lot of research, trying many options and making another questions, I finally found a workaround that accomplish all the requirements for the report. As I said, is kind of a workaround, but it can be usefull for any who could be struggling with JasperReports.

1.- Reports

First of all, I made 2 independent reports, each with their own query and sections. Each report had a parameter named PAGE_COUNT_OFFSET of type Integer. In the page footer, where I need to put the pagination I did the following:

Main report

<pageFooter>
    <band height="54" splitType="Stretch">
        <textField>
            <reportElement x="146" y="2" width="100" height="30" uuid="1314c392-e24a-47bd-a0aa-6b19803be36a"/>
            <textElement textAlignment="Right"/>
            <textFieldExpression><![CDATA["- Page " + $V{PAGE_NUMBER}]]></textFieldExpression>
        </textField>
        <textField evaluationTime="Report">
            <reportElement x="246" y="2" width="100" height="30" uuid="230a6e2d-3e6d-4d52-9228-aab519122537"/>
            <textElement textAlignment="Left"/>
            <textFieldExpression><![CDATA[" of " + ($V{PAGE_NUMBER} + $P{PAGE_COUNT_OFFSET}) + " -"]]></textFieldExpression>
        </textField>
    </band>
</pageFooter>

Subreport

<pageFooter>
    <band height="50">
        <property name="com.jaspersoft.studio.unit.height" value="pixel"/>
        <textField>
            <reportElement x="146" y="2" width="100" height="30" uuid="b6a836b2-41f5-4a61-af64-50720544cef2"/>
            <textElement textAlignment="Right"/>
            <textFieldExpression><![CDATA["- Page " + ($V{PAGE_NUMBER} + $P{PAGE_COUNT_OFFSET})]]></textFieldExpression>
        </textField>
        <textField evaluationTime="Report">
            <reportElement x="246" y="2" width="100" height="30" uuid="be5469d3-10ed-4deb-964d-c1f9c9b7337a"/>
            <textElement textAlignment="Left"/>
            <textFieldExpression><![CDATA[" of " + ($V{PAGE_NUMBER} + $P{PAGE_COUNT_OFFSET}) + " -"]]></textFieldExpression>
        </textField>
    </band>
</pageFooter>

Note: I have to thank to Petter Friberg for this answer, it really helped me a lot.

2.- Report filling

For this process to be successful, It's not necesary to compile the report in every execution, you just need the compiled .jasper file to be filled with data. I did this programmaticaly with a Java routine. I'll show and example with a main method, in practice you'll be writing this logic into a method body or something like that.

Note: I'm assuming the .jasper files are packaged in the root of the JAR file.

public static void main(String[] args) {
    try {
        // Build parameters map with fake offset
        Map<String, Object> subreportParams = new HashMap<>();
        // PARAM_PAGE_COUNT_OFFSET is a convenient String constant with the parameter name for the report
        subreportParams.put(PARAM_PAGE_COUNT_OFFSET, 10);

        Map<String, Object> mainParams = new HashMap<>(subreportParams);
        mainParams.put(...); // Some other parameters

        // Fill the report the first time and get the real page count for every report
        ClassLoader classLoader = getClass().getClassLoader();
        // Again, MAIN_REPORT_FILE and SUBREPORT_FILE are String constants containing jasper files names
        // JdbcManager.getConnection() is a utility method which gets a connection with predefined parameters
        JasperPrint main = JasperFillManager.fillReport(classLoader.getResourceAsStream(MAIN_REPORT_FILE), mainParams, JdbcManager.getConnection());
        JasperPrint subreport = JasperFillManager.fillReport(classLoader.getResourceAsStream(SUBREPORT_FILE), subreportParams, JdbcManager.getConnection());

        // Get the page count for every report and reinsert it in the parameters map
        int mainPageCount = main.getPages().size();
        int subreportPageCount = subreport.getPages().size();
        // The offset for the given report should be the count from the other
        mainParams.put(PARAM_PAGE_COUNT_OFFSET, subreportPageCount);
        subreportParams.put(PARAM_PAGE_COUNT_OFFSET, mainPageCount);

        // Fill with the final parameters and generates a JpList object
        main = JasperFillManager.fillReport(classLoader.getResourceAsStream(MAIN_REPORT_FILE), mainParams, JdbcManager.getConnection());
        subreport = JasperFillManager.fillReport(classLoader.getResourceAsStream(SUBREPORT_FILE), subreportParams, JdbcManager.getConnection());
        List<JasperPrint> finalReport = new ArrayList<>();
        finalReport.add(main);
        finalReport.add(subreport);

        // Export the report and save it to a given path
        JRPdfExporter exporter = new JRPdfExporter();
        exporter.setExporterInput(SimpleExporterInput.getInstance(finalReport));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(
                new FileOutputStream("path/to/report.pdf")));
        exporter.exportReport();
    } catch (JRException ex) {
        LOGGER.log(Level.SEVERE, "Error generating report", ex);
    } catch (FileNotFoundException ex) {
        LOGGER.log(Level.SEVERE, "Error saving file", ex);
    }
}

This way I obtained my report in a single PDF and paginated correctly. Hope that helps anyone. Best regards.

Upvotes: 0

localghost
localghost

Reputation: 419

You could add a dummy group that won't generate any ruptures and place the second detail's content inside the group footer band.

Upvotes: 0

Related Questions