当前位置: 首页 > news >正文

java通过模板渲染PDF报告

今天研究一下如何根据pdf模板渲染生成pdf文件,例如:导出公司资金年度报告。废话不多说,我先把代码端上来。

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.test</groupId><artifactId>pdf-template</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- FastJSON --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><!-- OpenHTML to PDF --><dependency><groupId>com.openhtmltopdf</groupId><artifactId>openhtmltopdf-core</artifactId><version>1.0.10</version></dependency><dependency><groupId>com.openhtmltopdf</groupId><artifactId>openhtmltopdf-pdfbox</artifactId><version>1.0.10</version></dependency><!-- 用于HTML解析和模板处理 --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.3</version></dependency><!-- 工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build></project>

创建报告类

import com.alibaba.fastjson.JSONObject;public class ReportData {// 公司名private String companyName;// 年度private int fiscalYear;// 总收入private double totalRevenue;// 总支出private double totalExpenses;// 净利润private double netProfit;// 总资产private double assets;// 总负债private double liabilities;// 所有者权益private double equity;public static ReportData fromJSONObject(JSONObject json) {ReportData data = new ReportData();data.setCompanyName(json.getString("companyName"));data.setFiscalYear(json.getInteger("fiscalYear"));data.setTotalRevenue(json.getDouble("totalRevenue"));data.setTotalExpenses(json.getDouble("totalExpenses"));data.setNetProfit(json.getDouble("netProfit"));data.setAssets(json.getDouble("assets"));data.setLiabilities(json.getDouble("liabilities"));data.setEquity(json.getDouble("equity"));return data;}// Getter和Setter方法public String getCompanyName() { return companyName; }public void setCompanyName(String companyName) { this.companyName = companyName; }public int getFiscalYear() { return fiscalYear; }public void setFiscalYear(int fiscalYear) { this.fiscalYear = fiscalYear; }public double getTotalRevenue() { return totalRevenue; }public void setTotalRevenue(double totalRevenue) { this.totalRevenue = totalRevenue; }public double getTotalExpenses() { return totalExpenses; }public void setTotalExpenses(double totalExpenses) { this.totalExpenses = totalExpenses; }public double getNetProfit() { return netProfit; }public void setNetProfit(double netProfit) { this.netProfit = netProfit; }public double getAssets() { return assets; }public void setAssets(double assets) { this.assets = assets; }public double getLiabilities() { return liabilities; }public void setLiabilities(double liabilities) { this.liabilities = liabilities; }public double getEquity() { return equity; }public void setEquity(double equity) { this.equity = equity; }
}

创建pdf渲染类

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import com.openhtmltopdf.extend.FSSupplier;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class PdfGenerator {public void generatePdf(ReportData data, String outputPath) throws Exception {// 读取HTML模板InputStream templateStream = getClass().getClassLoader().getResourceAsStream("template/report-template.html");if (templateStream == null) {throw new FileNotFoundException("HTML模板文件未找到");}String htmlTemplate = IOUtils.toString(templateStream, "UTF-8");// 替换模板中的变量String processedHtml = processTemplate(htmlTemplate, data);// 使用Jsoup清理HTML,确保格式正确Document doc = Jsoup.parse(processedHtml);doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml);String cleanHtml = doc.html();// 使用OpenHTMLToPDF生成PDF,配置中文字体OutputStream os = null;try {os = new FileOutputStream(outputPath);PdfRendererBuilder builder = new PdfRendererBuilder();// 配置中文字体,如果字体没有则需要下载InputStream fontStream = getClass().getClassLoader().getResourceAsStream("fonts/simsun.ttf");if (fontStream != null) {byte[] fontBytes = IOUtils.toByteArray(fontStream);FSSupplier<InputStream> fontSupplier = () -> new ByteArrayInputStream(fontBytes);builder.useFont(fontSupplier, "SimSun", 400, BaseRendererBuilder.FontStyle.NORMAL, true);builder.useFont(fontSupplier, "SimSun", 400, BaseRendererBuilder.FontStyle.ITALIC, true);builder.useFont(fontSupplier, "SimSun", 700, BaseRendererBuilder.FontStyle.OBLIQUE, true);}builder.withHtmlContent(cleanHtml, null);builder.useFastMode();builder.toStream(os);builder.run();} finally {if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}}}private String processTemplate(String html, ReportData data) {// 创建数字格式化器DecimalFormat df = new DecimalFormat("#,##0.00");// 准备替换变量Map<String, String> variables = new HashMap<String, String>();variables.put("companyName", data.getCompanyName());variables.put("fiscalYear", String.valueOf(data.getFiscalYear()));variables.put("totalRevenueFormatted", df.format(data.getTotalRevenue()));variables.put("totalExpensesFormatted", df.format(data.getTotalExpenses()));variables.put("netProfitFormatted", df.format(data.getNetProfit()));variables.put("assetsFormatted", df.format(data.getAssets()));variables.put("liabilitiesFormatted", df.format(data.getLiabilities()));variables.put("equityFormatted", df.format(data.getEquity()));// 计算财务比率double profitMargin = (data.getNetProfit() / data.getTotalRevenue()) * 100;double debtToAssetRatio = (data.getLiabilities() / data.getAssets()) * 100;double returnOnEquity = (data.getNetProfit() / data.getEquity()) * 100;variables.put("profitMargin", String.format("%.2f", profitMargin));variables.put("debtToAssetRatio", String.format("%.2f", debtToAssetRatio));variables.put("returnOnEquity", String.format("%.2f", returnOnEquity));// 根据数值正负设置CSS类variables.put("netProfitClass", data.getNetProfit() >= 0 ? "positive" : "negative");variables.put("profitMarginClass", profitMargin >= 0 ? "positive" : "negative");variables.put("returnOnEquityClass", returnOnEquity >= 0 ? "positive" : "negative");// 添加生成日期SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");variables.put("generationDate", sdf.format(new Date()));// 替换所有变量String result = html;for (Map.Entry<String, String> entry : variables.entrySet()) {String value = StringUtils.defaultString(entry.getValue(), "");result = result.replace("${" + entry.getKey() + "}", value);}return result;}
}

创建Main测试渲染pdf

import com.alibaba.fastjson.JSONObject;
import com.test.builder.PdfGenerator;
import com.test.builder.ReportData;import java.io.File;public class Main {public static void main(String[] args) {try {// 示例JSON数据JSONObject jsonData = new JSONObject();jsonData.put("companyName", "示例科技有限公司");jsonData.put("fiscalYear", 2023);jsonData.put("totalRevenue", 12500000.75);jsonData.put("totalExpenses", 8500000.50);jsonData.put("netProfit", 4000000.25);jsonData.put("assets", 8500000.00);jsonData.put("liabilities", 3000000.00);jsonData.put("equity", 5500000.00);// 从JSON创建数据对象ReportData reportData = ReportData.fromJSONObject(jsonData);// 生成PDFPdfGenerator generator = new PdfGenerator();String outputPath = new File("").getAbsolutePath() + "/企业资金年度报告.pdf";generator.generatePdf(reportData, outputPath);System.out.println("PDF生成成功,保存路径: " + outputPath);} catch (Exception e) {e.printStackTrace();}}
}

最重要的模板文件

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8" /><style>@page {size: A4;margin: 2cm;}body {font-family: SimSun, Arial, sans-serif;line-height: 1.6;color: #333;margin: 0;padding: 0;}.header {text-align: center;margin-bottom: 30px;border-bottom: 2px solid #1a5276;padding-bottom: 20px;}.company-name {font-size: 24pt;font-weight: bold;color: #1a5276;margin-bottom: 10px;}.report-title {font-size: 18pt;margin-bottom: 5px;}.fiscal-year {font-size: 14pt;}.section {margin-bottom: 25px;}.section-title {font-size: 16pt;font-weight: bold;color: #1a5276;border-left: 5px solid #1a5276;padding-left: 10px;margin: 20px 0 15px 0;}.financial-table {width: 100%;border-collapse: collapse;margin: 15px 0;font-family: SimSun, Arial, sans-serif;}.financial-table th, .financial-table td {border: 1px solid #ddd;padding: 10px;text-align: right;}.financial-table th {background-color: #f2f2f2;text-align: left;font-weight: bold;}.financial-table tr:nth-child(even) {background-color: #f9f9f9;}.highlight {font-weight: bold;color: #1a5276;}.footer {margin-top: 50px;text-align: right;font-size: 10pt;color: #666;}.signature-area {margin-top: 60px;border-top: 1px dashed #999;padding-top: 10px;}.positive {color: #28a745;}.negative {color: #dc3545;}</style>
</head>
<body>
<div class="header"><div class="company-name">${companyName}</div><div class="report-title">企业资金年度报告</div><div class="fiscal-year">财政年度: ${fiscalYear}</div>
</div><div class="section"><div class="section-title">财务概要</div><table class="financial-table"><tr><th>指标</th><th>金额 (元)</th></tr><tr><td>总收入</td><td>${totalRevenueFormatted}</td></tr><tr><td>总支出</td><td>${totalExpensesFormatted}</td></tr><tr class="highlight ${netProfitClass}"><td>净利润</td><td>${netProfitFormatted}</td></tr></table>
</div><div class="section"><div class="section-title">资产负债表</div><table class="financial-table"><tr><th>项目</th><th>金额 (元)</th></tr><tr><td>总资产</td><td>${assetsFormatted}</td></tr><tr><td>总负债</td><td>${liabilitiesFormatted}</td></tr><tr class="highlight"><td>所有者权益</td><td>${equityFormatted}</td></tr></table>
</div><div class="section"><div class="section-title">财务分析</div><p>基于${fiscalYear}年度的财务数据,${companyName}的财务状况如下:</p><p>净利润率为: <span class="${profitMarginClass}">${profitMargin}%</span></p><p>资产负债率为: ${debtToAssetRatio}%</p><p>权益回报率为: <span class="${returnOnEquityClass}">${returnOnEquity}%</span></p>
</div><div class="signature-area"><p>总经理签字: ________________________</p><p>财务总监签字: ________________________</p><p>日期: ________________________</p>
</div><div class="footer">本报告生成时间: ${generationDate}
</div>
</body>
</html>

我在网上找过很多种通过生成pdf的方式,但是要么先下载一个软件定义pdf模板文件然后通过代码实现模板渲染,要么就是通过工具如iText硬写,实在复杂。这个方式是我找了多种实现方式以及询问AI,类比这些实现方式得到的我觉得我最喜欢的方式。因为我也会写一点前端代码,而且这个方式不算太复杂,也不需要下载什么软件就先去定义一个pdf模板文件,希望对各位有所帮助。

http://www.dtcms.com/a/564463.html

相关文章:

  • 基于 Verl 前端与 Atlas A3 集群的 DeepSeek-R1 模型 RL 训练优化实践:Cann-recipes-train 仓库技术深度解读
  • 技术评测:六行神算大模型平台实战分析
  • SecureShellProtocol(ssh)
  • HAproxy负载均衡详细介绍
  • Rust编程学习 - 如何快速构建一个单线程 web server
  • 1、PCB导入Siwave并设置叠层数据
  • 网站做生鲜线下推广建议舆情通
  • 国产电脑可以装windows吗_国产电脑安装windows要求及方法
  • Linux 基本指令入门:从基础操作到实战应用
  • 专门做任务的网站吗查看网站流量
  • 建设一个招聘网站大概多少费用wordpress主题应该怎么添加
  • flink 核心
  • 《LLMmap: Fingerprinting for Large Language Models》论文阅读
  • 节点小宝4.0版本功能升级预告:简化远程操作,优化用户体验
  • 基于卷积神经网络的作物病害识别系统(论文+源码)
  • Vue预览Excel文件的完整指南:从零开始实现
  • 黄金网站下载免费wordpress 邮箱发布
  • Min浏览器项目启动与打包
  • AWS云计算入门指南:从零到一,详解核心服务与免费套餐
  • 1千万人网站维护成本p2p网站功能模块
  • 网站做app有什么意义网站有死链怎么办
  • 网站做的不满意wordpress哪些插件
  • 邯郸企业做网站方案官方软件下载大全
  • 做民宿推广都有哪些网站运营的网站
  • 外贸网站运营工作内容西安网页设计师
  • 网站在正在建设中泰康人寿保险官方网站
  • 所有复刻手表网站百度网盘搜索引擎入口在哪
  • 网站服务器诊断深圳插画设计公司
  • 自己做qq头像的网站wordpress主题怎么改
  • 一个域名可以建设几个网站cmmi软件开发流程