Files
agent_jrxml/lib/java/JrxmlRenderer.java
panda bb6cc6e241 feat: add Java JRXML-to-PNG rendering pipeline with pixel-level SSIM comparison
- lib/java/: Java renderer (JrxmlRenderer) using JasperReports 6.21.0
  - JrxmlDebug for diagnostics, JrxmlGen for format reference
  - download_jars.sh for one-time dependency setup
- agent/nodes.py: _render_jrxml_to_png() and _compute_pixel_similarity()
  - Pixel comparison integrates into validate node (SSIM < 0.4 fails)
  - Pixel fidelity context injected into correct_jrxml for targeted fixes
- tests/test_pixel_comparison.py: 15 unit tests (render, SSIM, integration)
- .gitignore: exclude lib/java/*.jar, lib/java/*.class, tmp/
- CLAUDE.md: v11 changelog documenting the rendering pipeline
- All non-LLM tests pass (97/97)
2026-05-23 15:09:55 +08:00

111 lines
4.2 KiB
Java

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.export.*;
import java.io.*;
import java.util.*;
import java.awt.image.*;
import javax.imageio.*;
/**
* Minimal JRXML → PNG renderer for pixel-level fidelity comparison.
* Usage: java JrxmlRenderer input.jrxml output.png [scale]
* scale: optional zoom factor (default 2.0 for readable text)
*
* Uses JasperReports 7.x exporter API (SimpleExporterInput / SimpleGraphics2DExporterOutput).
*/
public class JrxmlRenderer {
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.err.println("Usage: java JrxmlRenderer <input.jrxml> <output.png> [scale]");
System.exit(1);
}
String jrxmlPath = args[0];
String outputPath = args[1];
float scale = args.length >= 3 ? Float.parseFloat(args[2]) : 2.0f;
File jrxmlFile = new File(jrxmlPath);
if (!jrxmlFile.exists()) {
System.err.println("ERROR: JRXML file not found: " + jrxmlPath);
System.exit(2);
}
try {
// 1. Compile JRXML → JasperReport
JasperReport report = JasperCompileManager.compileReport(jrxmlPath);
// 2. Fill with empty data source
Map<String, Object> params = new HashMap<>();
JasperPrint print = JasperFillManager.fillReport(report, params, new JREmptyDataSource());
int pages = print.getPages().size();
if (pages == 0) {
System.err.println("ERROR: Report has 0 pages after filling");
System.exit(4);
}
// 3. Calculate image dimensions from page size
// JasperReports uses 72 DPI, convert to pixels with scale
int pageWidth = (int) Math.ceil(report.getPageWidth() * scale);
int pageHeight = (int) Math.ceil(report.getPageHeight() * scale);
// 4. Render each page to a BufferedImage
java.util.List<BufferedImage> pageImages = new ArrayList<>();
for (int i = 0; i < pages; i++) {
BufferedImage pageImage = new BufferedImage(pageWidth, pageHeight, BufferedImage.TYPE_INT_RGB);
java.awt.Graphics2D g2d = pageImage.createGraphics();
g2d.setColor(java.awt.Color.WHITE);
g2d.fillRect(0, 0, pageWidth, pageHeight);
// Scale the graphics context
g2d.scale(scale, scale);
JRGraphics2DExporter exporter = new JRGraphics2DExporter();
exporter.setExporterInput(new SimpleExporterInput(print));
SimpleGraphics2DExporterOutput output = new SimpleGraphics2DExporterOutput();
output.setGraphics2D(g2d);
exporter.setExporterOutput(output);
SimpleGraphics2DReportConfiguration config = new SimpleGraphics2DReportConfiguration();
config.setPageIndex(i);
exporter.setConfiguration(config);
exporter.exportReport();
g2d.dispose();
pageImages.add(pageImage);
}
// 5. Combine pages into single tall image
int totalHeight = 0;
for (BufferedImage img : pageImages) {
totalHeight += img.getHeight();
}
BufferedImage combined = new BufferedImage(pageWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
java.awt.Graphics2D g = combined.createGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, pageWidth, totalHeight);
int yOffset = 0;
for (BufferedImage img : pageImages) {
g.drawImage(img, 0, yOffset, null);
yOffset += img.getHeight();
}
g.dispose();
// 6. Write PNG
ImageIO.write(combined, "png", new File(outputPath));
System.out.println("OK: " + outputPath
+ " (pages=" + pages
+ ", size=" + pageWidth + "x" + totalHeight
+ ", scale=" + scale + ")");
} catch (JRException e) {
System.err.println("JASPER_ERROR: " + e.getMessage());
e.printStackTrace();
System.exit(3);
}
}
}