java通过模板实现渲染PDF报告
目录
- 引入依赖
- 创建报告类
- 创建pdf渲染类
- 创建Main测试渲染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();
android } 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> entryjavascript : 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);
// 生成PDF
PdfGenerator generator = new PdfGenerator();
String outputPath = new File("").getAbsolutePath() + "/企业资金年度报告.pdf";
generator.generatePdf(reportData, output编程客栈Path);
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>
python <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="pythonsection-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模板文件,希望对各位有所帮助。
到此这篇关于java通过模板实现渲染PDF报告的文章就介绍到这了,更多相关java渲染PDF内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论