Testing DynamicReports 6.20: Grouped Totals and Encryption
Grouped totals that split in two, an encrypted export that needs an extra jar, HTML that loses its tables, and a dependency tree with remote code execution advisories are the issues we hit when we put DynamicReports 6.20.1 through a set of test reports on Java 17. The library builds JasperReports report designs through a fluent API in Java, and 6.20.1, published on 21 November 2023, is its newest release. Each issue below comes with the run or the advisory record behind it.
We work at Iron Software, and the end of this article covers our library, IronPDF.
Grouped Totals Split When Rows Arrive Unsorted
A DynamicReports group breaks every time the grouping value changes from one row to the next. It does not collect matching rows together, so the order of the data decides the layout. The listing below groups four orders by customer, with one Harbor Supply order arriving last.
import static net.sf.dynamicreports.report.builder.DynamicReports.col; import static net.sf.dynamicreports.report.builder.DynamicReports.grp; import static net.sf.dynamicreports.report.builder.DynamicReports.report; import static net.sf.dynamicreports.report.builder.DynamicReports.sbt; import static net.sf.dynamicreports.report.builder.DynamicReports.type; import java.io.FileOutputStream; import java.io.OutputStream; import java.math.BigDecimal; import net.sf.dynamicreports.report.builder.column.TextColumnBuilder; import net.sf.dynamicreports.report.builder.group.ColumnGroupBuilder; import net.sf.dynamicreports.report.datasource.DRDataSource; public class UnsortedGroupReport { public static void main(String[] args) throws Exception { TextColumnBuilder<String> customer = col.column("Customer", "customer", type.stringType()); TextColumnBuilder<String> orderId = col.column("Order", "orderId", type.stringType()); TextColumnBuilder<BigDecimal> amount = col.column("Amount", "amount", type.bigDecimalType()); // The last row belongs to Harbor Supply but arrives after the Northwind Tools rows. DRDataSource data = new DRDataSource("customer", "orderId", "amount"); data.add("Harbor Supply", "A-100", new BigDecimal("1250.00")); data.add("Northwind Tools", "A-101", new BigDecimal("86.40")); data.add("Northwind Tools", "A-103", new BigDecimal("442.10")); data.add("Harbor Supply", "A-102", new BigDecimal("310.75")); ColumnGroupBuilder byCustomer = grp.group(customer); try (OutputStream out = new FileOutputStream("orders-unsorted.pdf")) { report() .columns(customer, orderId, amount) .groupBy(byCustomer) .subtotalsAtGroupFooter(byCustomer, sbt.count(orderId), sbt.sum(amount)) .setDataSource(data) .toPdf(out); } } } The PDF holds three groups instead of two. Harbor Supply appears first with one order and a subtotal of 1,250.00, Northwind Tools follows with two orders and 528.50, and a second Harbor Supply group closes the page with one order and 310.75. Nothing in the run warns about it. Any report fed from a query without a matching ORDER BY can print a customer twice with two partial subtotals. Adding sortBy(customer) to the builder, or sorting the query, merged the two Harbor Supply orders into a single group (2 orders, 1,560.75) in a follow-up run.
The same class of quiet output change shows up in number columns. type.doubleType() formats with the pattern #,##0.#, so a double price of 8.99 printed as 9 in a separate run, while type.bigDecimalType() keeps at least two decimal places.
Encrypted Export Needs Bouncy Castle and a 128-Bit Switch
Encryption is configured on a JasperPdfExporterBuilder. The listing below sets metadata, both passwords, a print permission and the 128-bit key flag, then writes the JasperReports design out as JRXML.
import static net.sf.dynamicreports.report.builder.DynamicReports.col; import static net.sf.dynamicreports.report.builder.DynamicReports.export; import static net.sf.dynamicreports.report.builder.DynamicReports.report; import static net.sf.dynamicreports.report.builder.DynamicReports.type; import java.io.FileOutputStream; import java.io.OutputStream; import java.math.BigDecimal; import net.sf.dynamicreports.jasper.builder.JasperReportBuilder; import net.sf.dynamicreports.jasper.builder.export.JasperPdfExporterBuilder; import net.sf.dynamicreports.jasper.constant.PdfPermission; import net.sf.dynamicreports.report.datasource.DRDataSource; public class ProtectedOrderExport { public static void main(String[] args) throws Exception { DRDataSource data = new DRDataSource("orderId", "amount"); data.add("A-100", new BigDecimal("1250.00")); data.add("A-101", new BigDecimal("86.40")); JasperReportBuilder orders = report() .columns( col.column("Order", "orderId", type.stringType()), col.column("Amount", "amount", type.bigDecimalType())) .setDataSource(data); // An exporter builder carries the metadata and encryption settings that toPdf(OutputStream) leaves at defaults. // Encryption needs bcprov-jdk18on on the classpath, and setBitKey128 lifts the 40-bit default key. JasperPdfExporterBuilder pdf = export.pdfExporter("orders-protected.pdf") .setMetadataTitle("Open orders") .setMetadataAuthor("Accounts team") .setEncrypted(true) .setBitKey128(true) .setUserPassword("reader") .setOwnerPassword("owner") .permissions(PdfPermission.PRINTING); orders.toPdf(pdf); // The same builder can also write out the JasperReports design it generated. try (OutputStream jrxml = new FileOutputStream("orders.jrxml")) { orders.toJrXml(jrxml); } } } Two things stand between this listing and a working file. With only dynamicreports-core declared, the first encrypted export threw NoClassDefFoundError for a Bouncy Castle class from inside OpenPDF's PdfEncryption, because OpenPDF 1.3.30.jaspersoft.3, the PDF writer JasperReports 6.20.6 uses, lists Bouncy Castle as an optional dependency. Adding org.bouncycastle:bcprov-jdk18on 1.86 fixed that. The second is the key length. Without setBitKey128(true), a run of the same exporter wrote an encryption dictionary with V 1 and R 2, which is 40-bit RC4. With the flag set as above, the dictionary moves to version 2, revision 3 and a 128-bit RC4 key, and the file opens with the password reader.
Teams that protect payslips or statements this way should check both settings in their build, since the missing class only shows up when an encrypted export runs, and the key length only in the file's encryption dictionary. The walkthrough on password protecting a PDF in Java shows how a password and print and copy restrictions are applied to a file.
What Happens to HTML Passed Through Markup.HTML?
DynamicReports has no HTML or CSS input. A report is a chain of builder calls, and the only place HTML enters is a text field whose markup is set to Markup.HTML.
import static net.sf.dynamicreports.report.builder.DynamicReports.cmp; import static net.sf.dynamicreports.report.builder.DynamicReports.report; import java.io.FileOutputStream; import java.io.OutputStream; import net.sf.dynamicreports.report.constant.Markup; public class HtmlMarkupReport { public static void main(String[] args) throws Exception { // A text field with HTML markup is the closest the builder gets to HTML input. String html = "<b>Invoice 1042</b> <i>due 30 November</i>" + "<table><tr><td>Widget</td><td>120</td></tr></table>" + "<div style=\"display:flex\"><span>Left</span><span>Right</span></div>"; try (OutputStream out = new FileOutputStream("html-markup.pdf")) { report() .title(cmp.text(html).setMarkup(Markup.HTML)) .toPdf(out); } } } The bold and italic runs came through in Helvetica-Bold and Helvetica-Oblique. The table did not. Widget and 120 printed on two separate lines at the left margin, and the flex container printed LeftRight, which is also what a browser gives two unspaced spans, so this short test says nothing about flex either way. A team with existing HTML invoice or statement templates would have to rebuild each one as a report design, in Java or through a JRXML file.
Remote Code Execution Advisories Arrive With JasperReports
Declaring dynamicreports-core 6.20.1 alone in an empty Maven project pulls in JasperReports 6.20.6, because the parent POM's jasperreportsVersion property says so. OSV lists nothing against DynamicReports itself on 4 November 2026, but the resolved tree matches a longer list, which starts with these.
- CVE-2025-10492 (GHSA-7c3f-cg9x-f3gr), CVSS 3.1 score 9.8 (NIST): A Java deserialization flaw in JasperReports 6.20.6 that NVD describes as allowing remote code execution through externally supplied data. The CNA gives 8.7 High under CVSS 4.0, and 7.0.4 clears it.
- CVE-2026-6009 (GHSA-9wxq-mwqw-8hhg), CVSS 4.0 score 8.7 (CNA): Another deserialization flaw in that JasperReports build, which NVD says can lead to remote code execution. NVD analysis is still pending, and 7.0.7 clears it.
- CVE-2025-48734, CVSS 3.1 score 8.8 (CISA ADP): An access control issue that commons-beanutils 1.9.4 matches, cleared by 1.11.0.
- CVE-2025-52999, CVSS 4.0 score 8.7 (CNA): Deeply nested input overflows the stack in jackson-core 2.14.1, cleared by 2.15.0.
- CVE-2025-48924 and CVE-2024-47554, CVSS 3.1 scores 5.3 and 4.3 (CISA ADP): They match commons-lang3 3.13.0 and commons-io 2.11.0, cleared by 3.18.0 and 2.14.0.
Eleven further GHSA entries from the June to September 2026 window match jackson-databind 2.14.1. CVE.org also holds CVE-2026-110879 for that version, scored 9.8 by HeroDevs and cleared by 2.18.12, which describes command execution where Spring is on the classpath and which an OSV package query misses. The same tree falls inside CVE-2026-89425 for jackson-core (7.5 from HeroDevs, cleared by 2.18.11) and CVE-2026-78254 for ant 1.7.1 (7.4 from CISA ADP, cleared by 1.10.18). NVD marks none of these CVEs as disputed. Both JasperReports fixes exist only on the 7.x line, so the last 6.x build on Maven Central, 6.21.5, sits inside both ranges as well. Maven's dependency management system decides which of these versions a real application ends up with, so the scan of the actual build is the one that counts.
Why Can a Version Pin Not Clear the JasperReports Advisories?
Pinning JasperReports 7.0.7 in dependencyManagement, with its separate jasperreports-pdf and jasperreports-json artifacts added, compiled the grouped report above cleanly against 6.20.1. The export did not run. Each attempt ended in NoClassDefFoundError with a zero-byte PDF, naming SimpleJsonMetadataReportConfiguration or SimplePdfReportConfiguration from net.sf.jasperreports.export depending on the run. JasperReports 7 moved both classes to new packages, so the advisory fix needs a DynamicReports build made for JasperReports 7.
That build has not been released. Maven Central's newest dynamicreports-core is still 6.20.1, and the GitHub releases page ends at the same tag. The default development branch carries 7.0.0-SNAPSHOT pinned to JasperReports 7.0.6, which clears the first advisory but not the second, and nothing has been committed to it since 30 March 2026. A user request for a 7.x build, issue #198, dates from 5 December 2024 and is still open. That leaves the 7.0.0-SNAPSHOT on Sonatype's snapshot repository, last redeployed on 30 October 2026, or a source build, either way with its JasperReports version bumped to 7.0.7 by hand, a route read from the POM and not run for this post. Both 6.20 releases also require Java 17, class file version 61, and teams on Java 8 or 11 stay on 6.12.1 from May 2021. DynamicReports is licensed under the LGPL-3.0.
Where These DynamicReports Issues Leave a Reporting Stack
Unsorted groups, rounded doubles, encryption that needs an extra jar and a flag, markup without tables, and two JasperReports advisories that a version pin cannot clear all come with DynamicReports 6.20.1.
IronPDF for Java takes a different route to a PDF report. It renders HTML with a Chromium engine, and in our 2026.11.3 run the markup test above kept Widget and 120 in one table row. Chart scripts run when setEnableJavaScript is on, with setWaitFor giving them time before the capture. The library resolves from Maven Central as com.ironsoftware:ironpdf, with no JasperReports in its POM and eleven 2026 releases, the latest 2026.11.3 on 3 November 2026.
The grouping and number formatting move into the Java code that writes the rows. The Java PDF report generator tutorial shows a report table written as an HTML string in Java, and getSecurity().setPassword() encrypted the finished file with a 128-bit RC4 key in our run. A free trial is enough to render one of your current reports from HTML and compare the files.
Has a split group or a rounded total turned up in your DynamicReports output, and what fixed it?
DynamicReports and JasperReports are trademarks of their respective owners, we have no affiliation with either project, and the facts above are based on the project's public repository, Maven Central, NVD, OSV, CVE.org and runs of each listing.
Drafted with AI. Code run and claims checked by the Iron Software DevRel team against net.sourceforge.dynamicreports:dynamicreports-core 6.20.1 and com.ironsoftware:ironpdf 2026.11.3 on 4 November 2026.
原文:https://www.a8q9.cn/en/article/testing-dynamicreports-6-20-grouped-totals-and-encryption-b10de4.html