如何在合并多个Jasper报表进行导出时重新计算页码?

6

我决定在收到聊天和评论时通过此问答解决如何处理“组合”jasper报告中的页码问题。

在Java中合并报告时,

JasperPrint jasperPrint1 = JasperFillManager.fillReport(report1, paramMap);
JasperPrint jasperPrint2 = JasperFillManager.fillReport(report2, paramMap);
List<JasperPrint> jasperPrintList = Arrays.asList(jasperPrint1, jasperPrint2); 

JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));

页面编号和总页数。
<textField>
    <reportElement x="435" y="30" width="80" height="20" uuid="14bad2ac-3a56-4cf4-b4dd-310b8fcd2120"/>
    <textElement textAlignment="Right"/>
    <textFieldExpression><![CDATA["Page "+$V{PAGE_NUMBER}+" of"]]></textFieldExpression>
</textField>
<textField evaluationTime="Report">
        <reportElement x="515" y="30" width="40" height="20" uuid="06567ba6-6243-43e9-9813-f6593528524c"/>
        <textFieldExpression><![CDATA[" " + $V{PAGE_NUMBER}]]></textFieldExpression>
</textField>

在我包含在列表中的每个报告中,都会重新启动,有没有办法可以重新计算这些报告?

我希望在每个页面上考虑我的最终组合报告,显示当前页面(和正确的总页数)。

4个回答

3

使用Jasper报表的方法不是在Java中合并报表,而是在主报表中包含不同的子报表,在此主报表中将页码移动。

然而,如果我们像问题一样在Java中进行合并,我们也可以使用Java来编辑和更正页码。

这个想法是在报告中设置“标记”,然后对JasperPrint进行后处理,用实际的页码替换标记。

示例

jrxml(毫不羞耻地使用freemaker风格)

<textField>
    <reportElement x="435" y="30" width="80" height="20" uuid="14bad2ac-3a56-4cf4-b4dd-310b8fcd2120"/>
    <textElement textAlignment="Right"/>
    <textFieldExpression><![CDATA["${CURRENT_PAGE_NUMBER}"]]></textFieldExpression>
</textField>
<textField evaluationTime="Report">
    <reportElement x="515" y="30" width="40" height="20" uuid="06567ba6-6243-43e9-9813-f6593528524c"/>
    <textFieldExpression><![CDATA["${TOTAL_PAGE_NUMBER}"]]></textFieldExpression>
</textField>

Java

定义我的标记

private static final String CURRENT_PAGE_NUMBER = "${CURRENT_PAGE_NUMBER}";
private static final String TOTAL_PAGE_NUMBER = "${TOTAL_PAGE_NUMBER}";

在我的报告中执行fillReport后,将所有标记替换为真实数字。

//First loop on all reports to get totale page number
int totPageNumber=0;
for (JasperPrint jp : jasperPrintList) {
    totPageNumber += jp.getPages().size();
}

//Second loop all reports to replace our markers with current and total number
int currentPage = 1;
for (JasperPrint jp : jasperPrintList) {
    List<JRPrintPage> pages = jp.getPages();
    //Loop all pages of report
    for (JRPrintPage jpp : pages) {
        List<JRPrintElement> elements = jpp.getElements();
        //Loop all elements on page
        for (JRPrintElement jpe : elements) {
            //Check if text element
            if (jpe instanceof JRPrintText){
                JRPrintText jpt = (JRPrintText) jpe;
                //Check if current page marker
                if (CURRENT_PAGE_NUMBER.equals(jpt.getValue())){
                    jpt.setText("Page " + currentPage + " of"); //Replace marker
                    continue;
                }
                //Check if totale page marker
                if (TOTAL_PAGE_NUMBER.equals(jpt.getValue())){
                    jpt.setText(" " + totPageNumber); //Replace marker
                }
            }
        }
        currentPage++;
    }
}

注意:如果这是我的项目中的代码,我不会嵌套过多的for和if语句,而是将其提取到不同的方法中,但为了本文的清晰度,我决定将其作为一个代码块发布。

现在已经准备好进行导出了。

JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
....

2
这是正确的,但如果存在JRPrintFrame,并且如果您的备注在此代码中,则无法找到它。下面是适合我的一个代码示例:(您应该找出TOTAL_PAGE_NUMBER在哪个容器元素中!)
private void correctPageNumbers(List<JasperPrint> jasperPrintList) {
// First loop on all reports to get totale page number
int totPageNumber = 0;
for (JasperPrint jp : jasperPrintList) {
    totPageNumber += jp.getPages().size();
}

// Second loop all reports to replace our markers with current and total
// number
int currentPage = 1;
for (JasperPrint jp : jasperPrintList) {
    List<JRPrintPage> pages = jp.getPages();
    // Loop all pages of report
    for (JRPrintPage jpp : pages) {
        List<JRPrintElement> elements = jpp.getElements();

        // Loop all elements on page
        for (JRPrintElement jpe : elements) {
            if (jpe instanceof JRSubreport) {
                JRSubreport jrr = (JRSubreport) jpe;
                jrr.getBackcolor();
            }
            // Check if text element
            if (jpe instanceof JRPrintText) {
                JRPrintText jpt = (JRPrintText) jpe;
                System.out.println("jpt.value: " + jpt.getValue() + "  |  " + "jpt.text" + jpt.getText());
                // Check if totale page marker
                if (TOTAL_PAGE_NUMBER.equals(jpt.getValue())) {
                    jpt.setText(" " + totPageNumber); // Replace marker
                }
            } else if (jpe instanceof JRTemplatePrintFrame) {
                List<JRPrintElement> frameElements = ((JRTemplatePrintFrame) jpe).getElements();
                for (JRPrintElement jpef : frameElements) {
                    if (jpef instanceof JRPrintText) {
                        JRPrintText jpt = (JRPrintText) jpef;
                        System.out.println("jpt.value: " + jpt.getValue() + "  |  " + "jpt.text"
                                + jpt.getText());
                        // Check if totale page marker
                        if (TOTAL_PAGE_NUMBER.equals(jpt.getValue())) {
                            jpt.setText(" " + totPageNumber); // Replace
                                                                // marker
                        }
                    }
                }
            }
        }
        currentPage++;
    }
}

}


谢谢,很不错的补充。 - Petter Friberg

1

我受到@Adityaz7答案的启发,我认为他的方法是最好的。但我已经做到了不需要将库添加到项目中并创建临时PDF文件。

首先修改您的模板并添加一个参数来保存之前页面的数量:

<parameter name="PREVIOUS_PAGE_NUMBER" class="java.lang.Integer"/>

在包含页面编号的报告元素中插入以下表达式:
<textFieldExpression><![CDATA[String.valueOf($V{PAGE_NUMBER} + $P{PREVIOUS_PAGE_NUMBER})]></textFieldExpression>

你需要使用String.valueOf()方法,因为如果你使用元素的简单求和$V{PAGE_NUMBER} + $P{PREVIOUS_PAGE_NUMBER},Jasper会将其处理为字符串的求和,并将两个数字作为连接输出。(例如,会打印11而不是2)
在你的Java代码中创建一个变量来保存前几页的数量,并将其作为参数传递给Jasper。然后填充第一个报告,简单地将包含报告页面的列表大小添加到prevPageNumber中,并将其作为参数传递给第二个报告,以此类推:
int prevPageNumber = 0;
HashMap<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("PREVIOUS_PAGE_NUMBER", prevPageNumber);

// fill the first report with prevPageNumber = 0
JasperPrint jasperPrint1 = JasperFillManager.fillReport(report1, paramMap);

if(jasperPrint1 != null && jasperPrint1.getPages() != null)
{   
    // add previous report page number
    prevPageNumber += jasperPrint1.getPages().size(); 
}

paramMap.put("PREVIOUS_PAGE_NUMBER", prevPageNumber);
JasperPrint jasperPrint2 = JasperFillManager.fillReport(report2, paramMap);

我将这个内联写法保留下来,以尊重提问者在问题中使用的结构,但如果你需要打印超过2份报告,最好使用循环来完成。

1
是的,这样做更好,不是因为依赖关系(因为Jasper报告已经有了,即使由于许可问题而转到另一个iText版本),而是因为您只想将其导出一次到PDF中(因此使用所有JasperPrint)。 - Petter Friberg

0

还有另一个简单的解决方案,不需要涉及像JasperPrint和JasperPages这样的所有jasper类。 这种方法是获取先前打印的页面数量,并将其添加到下一次打印的PAGE_NUMBER变量中。

假设我们有三个jasper打印。 jasperPrint1,jasperPrint2,jasperPrint3,因为jasperPrint1具有动态内容和可变数量的页面,很难知道下一个打印的页码。 解决方案是 -

将第一个打印导出到临时pdf文件中 -

JasperExportManager.exportReportToPdfFile(jasperPrint1, 
tempPdfFile.getAbsolutePath());

初始化一个变量,用于跟踪所有先前打印的页面,初始值为0;

pafeSofar = 0

编写一个方法来返回 PDF 文件中的页数:
private int numberOfPages(String filePath) throws IOException {
     PdfReader pdfReader = new PdfReader(filePath);
    return pdfReader.getNumberOfPages();
    }

这里的PdfReader是iText中的一个类。您可以添加Maven依赖项或手动下载.jar文件并将其添加到类路径中。
itext pdf reader的Maven依赖项如下:
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.0</version>
</dependency>

使用此方法获取上一个报告中的页面数量。
pagesSofar = pagesSofar + numberOfPages(tempPdfFile.getAbsolutePath());`

创建一个参数来传递这个变量,并将该参数添加到其他报表中的PAGE_NUMBER。
parameterMap.put("previousPageNumber", pagesSofar);`

在你的 .jrxml 文件中:

<textField>
<reportElement x="232" y="1" width="100" height="20"/>
<textFieldExpression><![CDATA[$V{PAGE_NUMBER}+$P{previousPageNumber}]]>
</textFieldExpression>
</textField>`

这里的 parameterMap 是您将在每个报告中传递的参数映射 -

JasperPrint jasperPrint2 = JasperFillManager.fillReport(jasperReport3, parameterMap, new JREmptyDataSource());

iText已经与JasperReport一起提供(即使是另一个版本也没有商业问题),唯一的问题是以这种方式多次导出PDF,我的想法是只导出一次,传递JasperPrint列表。 - Petter Friberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接