在导出时合并多个 jasper 报告时如何重新计算页码?
How to recalculate page number when combining multiple jasper reports in export?
在收到聊天中的两个问题和comment关于如何处理 "combined" jasper 报告中的页码的回答后,我决定通过此 Q/A =24=]
使用
合并 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>
对于我包含在列表中的每个报告总是重新启动,有没有办法可以重新计算这些?
考虑到我的最终合并报告,我希望在每一页上都有当前页面(以及正确的总页数)。
The jasper-report way would be to not combine reports in java but to use
a main report and include the different reports as subreports in this main
report, moving the page number to the main report.
但是,如果我们喜欢在java中合并,如题,我们也可以使用java来编辑和修改页码。
这个想法是在报告中设置“标记”,然后post-处理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 语句,而是将代码提取到不同的方法中,但为了 post 的清晰度,我决定 post 将其全部作为一个代码块
现在可以出口了
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
....
没错,但是如果有 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++;
}
}
}
还有另一个简单的解决方案,它不会像 JasperPrint 和 JasperPages 这样的所有 jasper 类。
方法是获取之前打印的页数并将其添加到下一次打印的 PAGE_NUMBER 变量中。
假设我们有三张碧玉版画。
jasperPrint1、jasperPrint2、jasperPrint3 并且因为 jasperPrint1 具有动态内容和变量号。很难知道下一次打印的页码。解决方案是 -
将第一个打印导出到临时 pdf 文件 -
JasperExportManager.exportReportToPdfFile(jasperPrint1,
tempPdfFile.getAbsolutePath());
初始化一个变量,用 0 跟踪所有先前打印的页面;
pafeSofar = 0
写一个方法给return没有。 pdf 文件中的页数:
private int numberOfPages(String filePath) throws IOException {
PdfReader pdfReader = new PdfReader(filePath);
return pdfReader.getNumberOfPages();
}
Here PdfReader is a class from iText. You can add a maven dependency or manually download a .jar and add it to your class path.
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());
@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);
我写这个内联是为了尊重问题中使用的结构 OP,但是如果要打印的报告超过 2 个,当然最好循环执行此操作。
在收到聊天中的两个问题和comment关于如何处理 "combined" jasper 报告中的页码的回答后,我决定通过此 Q/A =24=]
使用
合并 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>
对于我包含在列表中的每个报告总是重新启动,有没有办法可以重新计算这些?
考虑到我的最终合并报告,我希望在每一页上都有当前页面(以及正确的总页数)。
The jasper-report way would be to not combine reports in java but to use a main report and include the different reports as subreports in this main report, moving the page number to the main report.
但是,如果我们喜欢在java中合并,如题,我们也可以使用java来编辑和修改页码。
这个想法是在报告中设置“标记”,然后post-处理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 语句,而是将代码提取到不同的方法中,但为了 post 的清晰度,我决定 post 将其全部作为一个代码块
现在可以出口了
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
....
没错,但是如果有 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++;
}
}
}
还有另一个简单的解决方案,它不会像 JasperPrint 和 JasperPages 这样的所有 jasper 类。 方法是获取之前打印的页数并将其添加到下一次打印的 PAGE_NUMBER 变量中。
假设我们有三张碧玉版画。 jasperPrint1、jasperPrint2、jasperPrint3 并且因为 jasperPrint1 具有动态内容和变量号。很难知道下一次打印的页码。解决方案是 -
将第一个打印导出到临时 pdf 文件 -
JasperExportManager.exportReportToPdfFile(jasperPrint1,
tempPdfFile.getAbsolutePath());
初始化一个变量,用 0 跟踪所有先前打印的页面;
pafeSofar = 0
写一个方法给return没有。 pdf 文件中的页数:
private int numberOfPages(String filePath) throws IOException {
PdfReader pdfReader = new PdfReader(filePath);
return pdfReader.getNumberOfPages();
}
Here PdfReader is a class from iText. You can add a maven dependency or manually download a .jar and add it to your class path.
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());
@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);
我写这个内联是为了尊重问题中使用的结构 OP,但是如果要打印的报告超过 2 个,当然最好循环执行此操作。