简体   繁体   中英

JasperReport - Concatenating multiple report in the same page prints a white page

It's been a shile since I am trying to programmatically build multiple table withing the same report with Jasper, so I decided to use again the DynamicJasper library. (which despite finding it sometimes rather unclear, it's usually very convenient).

Anyways, I thought what I need is concatenating multiple table reports in the same report (or page).

The main "data source" I am using is a list of lists, every list is ofc a different "table" to be filled. A similar mechanism happends for those headers. (I'll show everything below, in a moment)

As far as I saw from the DynamicJasper, it acts in a rather simple and direct way by declaring some kind of DynamicReportBuilder with some general informations about the report (Title, whidth, style, ecc..) and then the addConcatenatedReport should make what I am looking for.

It's written also that every subreport MUST be identified by a "string" who will be a hook for the data source (AKA the filling data) which has to be specified lately within a Map of parameters.

Everything fine till here, but printing that whole results always in a white page!

See: http://dynamicjasper.com/2010/10/08/how-to-add-concatenated-subreports/

I'll post from now how I approached the problem, taking the above example as a guideline.

Starting from the last thing:

this.dynamicReport = new Generator().withTitle(this.title).withHeaders(this.headerData).prepare();
    Map generatedParams = this.buildParams();
    this.jasperReport = DynamicJasperHelper.generateJasperReport(this.dynamicReport, new ClassicLayoutManager(), generatedParams);
    this.jasperPrint = JasperFillManager.fillReport(this.jasperReport, generatedParams);

I create the Generator class, which is in charge of just generating the report structure (and the concatenated report as well).

Within the Generator there are 2 main functions:

public DynamicReport prepare() throws Exception {
    Style rowStyle = new Style();
    rowStyle.setBorder(Border.NO_BORDER());
    rowStyle.setBackgroundColor(Color.LIGHT_GRAY);
    rowStyle.setTransparency(ar.com.fdvs.dj.domain.constants.Transparency.OPAQUE);

    DynamicReportBuilder reportBuilder = new DynamicReportBuilder()
            .setTitleStyle(new Style())
            .setTitle(this.title)
            .setDetailHeight(15)
            .setLeftMargin(20)
            .setRightMargin(20)
            .setTopMargin(20)
            .setBottomMargin(20)
            .setPrintBackgroundOnOddRows(true)
            .setOddRowBackgroundStyle(rowStyle);

    for (HeaderData headerData : headerData) {
        reportBuilder.addConcatenatedReport(
                this.createSubreportForEntry(headerData),
                new ClassicLayoutManager(),
                headerData.getDataSourceName(), // <-------- see?
                DJConstants.DATA_SOURCE_ORIGIN_PARAMETER,
                DJConstants.DATA_SOURCE_TYPE_COLLECTION,
                false
        );
    }

    reportBuilder.setUseFullPageWidth(true);

    return reportBuilder.build();
}


private DynamicReport createSubreportForEntry(HeaderData headerData) throws Exception {
    FastReportBuilder reportBuilder = new FastReportBuilder();

    for (HeaderColumn headerColumn : headerData.getHeaderColumns()) {
        reportBuilder.addColumn(headerColumn.getColumnName(), headerColumn.getColumnCode(), String.class.getName(), headerColumn.getWidth());
    }

    return reportBuilder
            .setTitle(headerData.getBoxName())
            .setMargins(5, 5, 20, 20)
            .setUseFullPageWidth(true)
            .build();
}

As I said, those are giving the shape to what should be printed, and I even highligted the data source, so that you know where it is. (that should be fine, I guess)

Now, what are those HeaderData ?

public class HeaderData {
    private String boxName;
    private String dataSourceName;
    private List<HeaderColumn> headerColumns;
}

public class HeaderColumn {
    private String columnCode; // Hook for the datasource
    private String columnName; // what should be shown to the user
    private int width;
    private int height;
    private Style style;
    private Style headerStyle;
}

I had to create a dynamic structure for headers, because every table will have different columns!

And at last, the function that generated those params (which contains those filling collections):

private Map buildParams() {
    Map params = new HashMap<>();
    this.reportRows.forEach(tuple2 -> {
        if (tuple2._1 != null && tuple2._2 != null) {
            params.put(tuple2._1.toString(), tuple2._2);
        }
    });
    return params;
}

This is the outcome:

[
  key: DataSourceName1, value: List[Class1(prop1=1, prop2=2), Class1(...), Class1(...), ...],
  key: DataSourceName2, value: List[Class2(...), Class2(...), Class2(...), ...],
  key: DataSourceName3, value: List[Class3(...), Class3(...), Class3(...), ...],
]

As you see, every "value" is a list of objects of different class. This is due to the fact (again) that I don't have the same headers for every table!

I guess that each "property" (prop1, prop2, ecc..) are being linked together with the "columnCode" (as shown above, in the createSubreportForEntry )

Ofc, attributes within those classes reflect those HeaderColumn.columnCode .

AH! At the -very last- I'd like also to post how I print that:

    JRPdfExporter exporter = new JRPdfExporter();

    File outputFile = new File(path);
    File parentFile = new File(path).getParentFile();

    if (parentFile != null) {
        parentFile.mkdirs();
    }

    FileOutputStream fos = new FileOutputStream(outputFile);

    SimpleExporterInput simpleExporterInput = new SimpleExporterInput(jasperPrint);
    OutputStreamExporterOutput simpleOutputStreamExporterOutput = new SimpleOutputStreamExporterOutput(fos);

    exporter.setExporterInput(simpleExporterInput);
    exporter.setExporterOutput(simpleOutputStreamExporterOutput);

    exporter.exportReport();

I'm not sure if there's a even better way by using the pure JasperReport library, either if I can have an hybrid approach, eg: DynamicJasper for generating the report structure (the famous Generator class above), pure JasperReport for filling it.

NOTE: As I said at the beginning, the word "pogrammatically" means that using JRXML is out of scope (yeah, it would have been easier maybe)

EDIT:

By debugging net.sf.jasperreports.engine.fill.JRVerticalFiller#fillReport I see that it goes in an "if" branch for "no data", so I guess using a map for passing collections it's not the correct way (?)

jasper version I use: 6.11.0

It seems that almost nobody had ever tried to use this library before, so I recently came across the PDFBox from Apache, who did the job in a more easier way (for those of you that don't know this library -> https://pdfbox.apache.org/index.html ).

Wat I found out further -> https://github.com/vandeseer/easytable for creating even prettier tables in a even more simple way (very well documented as well!).

I'd suggest those two, to any of you searching for a dynamic way to generate tables in a PDF via code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM