java通過模板實現(xiàn)渲染PDF報告
今天研究一下如何根據(jù)pdf模板渲染生成pdf文件,例如:導(dǎo)出公司資金年度報告。廢話不多說,我先把代碼端上來。
引入依賴
<?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>
創(chuàng)建報告類
import com.alibaba.fastjson.JSONObject;
public class ReportData {
// 公司名
private String companyName;
// 年度
private int fiscalYear;
// 總收入
private double totalRevenue;
// 總支出
private double totalExpenses;
// 凈利潤
private double netProfit;
// 總資產(chǎn)
private double assets;
// 總負債
private double liabilities;
// 所有者權(quán)益
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; }
}
創(chuàng)建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) {
// 創(chuàng)建數(shù)字格式化器
DecimalFormat df = new DecimalFormat("#,##0.00");
// 準(zhǔn)備替換變量
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()));
// 計算財務(wù)比率
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));
// 根據(jù)數(shù)值正負設(shè)置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;
}
}
創(chuàng)建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數(shù)據(jù)
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創(chuàng)建數(shù)據(jù)對象
ReportData reportData = ReportData.fromJSONObject(jsonData);
// 生成PDF
PdfGenerator generator = new PdfGenerator();
String outputPath = new File("").getAbsolutePath() + "/企業(yè)資金年度報告.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">企業(yè)資金年度報告</div>
<div class="fiscal-year">財政年度: ${fiscalYear}</div>
</div>
<div class="section">
<div class="section-title">財務(wù)概要</div>
<table class="financial-table">
<tr>
<th>指標(biāo)</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">資產(chǎn)負債表</div>
<table class="financial-table">
<tr>
<th>項目</th>
<th>金額 (元)</th>
</tr>
<tr>
<td>總資產(chǎn)</td>
<td>${assetsFormatted}</td>
</tr>
<tr>
<td>總負債</td>
<td>${liabilitiesFormatted}</td>
</tr>
<tr class="highlight">
<td>所有者權(quán)益</td>
<td>${equityFormatted}</td>
</tr>
</table>
</div>
<div class="section">
<div class="section-title">財務(wù)分析</div>
<p>基于${fiscalYear}年度的財務(wù)數(shù)據(jù),${companyName}的財務(wù)狀況如下:</p>
<p>凈利潤率為: <span class="${profitMarginClass}">${profitMargin}%</span></p>
<p>資產(chǎn)負債率為: ${debtToAssetRatio}%</p>
<p>權(quán)益回報率為: <span class="${returnOnEquityClass}">${returnOnEquity}%</span></p>
</div>
<div class="signature-area">
<p>總經(jīng)理簽字: ________________________</p>
<p>財務(wù)總監(jiān)簽字: ________________________</p>
<p>日期: ________________________</p>
</div>
<div class="footer">
本報告生成時間: ${generationDate}
</div>
</body>
</html>
我在網(wǎng)上找過很多種通過生成pdf的方式,但是要么先下載一個軟件定義pdf模板文件然后通過代碼實現(xiàn)模板渲染,要么就是通過工具如iText硬寫,實在復(fù)雜。這個方式是我找了多種實現(xiàn)方式以及詢問AI,類比這些實現(xiàn)方式得到的我覺得我最喜歡的方式。因為我也會寫一點前端代碼,而且這個方式不算太復(fù)雜,也不需要下載什么軟件就先去定義一個pdf模板文件,希望對各位有所幫助。
到此這篇關(guān)于java通過模板實現(xiàn)渲染PDF報告的文章就介紹到這了,更多相關(guān)java渲染PDF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 獲取當(dāng)前函數(shù)名的實現(xiàn)代碼
以下是對使用java獲取當(dāng)前函數(shù)名的實現(xiàn)代碼進行了介紹。需要的朋友可以過來參考下2013-08-08
關(guān)于maven依賴 ${xxx.version}報錯問題
這篇文章主要介紹了關(guān)于maven依賴 ${xxx.version}報錯問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01

