java導出pdf(使用html)

引入maven

<dependencies><!-- Thymeleaf --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf</artifactId><version>3.1.1.RELEASE</version> <!-- 或與 Spring Boot 匹配的版本 --></dependency><!-- Flying Saucer PDF 渲染(使用 OpenPDFXHTMLRenderer--><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-openpdf</artifactId><version>9.1.22</version></dependency><!-- iText 2.x (lowagie) 用于合并 PDF 頁等操作 --><dependency><groupId>com.lowagie</groupId><artifactId>itext</artifactId><version>2.1.7</version></dependency><!-- Servlet API(如果你在非 Web 項目中操作 HttpServletResponse--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><!-- Lombok(用于注解 @Slf4j @SneakyThrows--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency>
</dependencies>

工具類

package com.tlzf.util;import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.List;
import java.util.Map;/*** pdf處理工具類** @author gourd.hu* @version 1.0findInstallationElevatorBlockVOById*/
@Slf4j
@Component
public class PdfUtil {private PdfUtil() {}/*** 按模板和參數生成html字符串,再轉換為flying-saucer識別的Document** @param templateName freemarker模板名稱x`* @param variables    freemarker模板參數* @return Document*/private static Document generateDoc(TemplateEngine templateEngine, String templateName, Map<String, Object> variables) {// 聲明一個上下文對象,里面放入要存到模板里面的數據final Context context = new Context();context.setVariables(variables);StringWriter stringWriter = new StringWriter();try (BufferedWriter writer = new BufferedWriter(stringWriter)) {templateEngine.process(templateName, context, writer);writer.flush();DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes("UTF-8")));} catch (Exception e) {log.error(e.getMessage(), e);return null;}}/*** 核心: 根據freemarker模板生成pdf文檔** @param templateEngine 配置* @param templateName   模板名稱* @param out            輸出流* @param listVars       模板參數* @throws Exception 模板無法找到、模板語法錯誤、IO異常*/private static void generateAll(TemplateEngine templateEngine, String templateName, OutputStream out, List<Map<String, Object>> listVars) throws Exception {if (CollectionUtils.isEmpty(listVars)) {log.warn("警告:模板參數為空!");return;}ITextRenderer renderer = new ITextRenderer();//設置字符集(宋體),此處必須與模板中的<body style="font-family: SimSun">一致,區分大小寫,不能寫成漢字"宋體"ITextFontResolver fontResolver = renderer.getFontResolver();//避免中文為空設置系統字體fontResolver.addFont("static/fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);fontResolver.addFont("static/fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);//根據參數集個數循環調用模板,追加到同一個pdf文檔中//(注意:此處從1開始,因為第0是創建pdf,從1往后則向pdf中追加內容)for (int i = 0; i < listVars.size(); i++) {Document docAppend = generateDoc(templateEngine, templateName, listVars.get(i));renderer.setDocument(docAppend, null);renderer.getSharedContext().setBaseURL(null);//展現和輸出pdfrenderer.layout();if (i == 0) {renderer.createPDF(out, false);} else {//寫下一個pdf頁面renderer.writeNextDocument();}}renderer.finishPDF(); //完成pdf寫入}private static void generateAll(TemplateEngine templateEngine, String templateName, OutputStream out, List<Map<String, Object>> listVars, String path) throws Exception {if (CollectionUtils.isEmpty(listVars)) {log.warn("警告:模板參數為空!");return;}ITextRenderer renderer = new ITextRenderer();//設置字符集(宋體),此處必須與模板中的<body style="font-family: SimSun">一致,區分大小寫,不能寫成漢字"宋體"ITextFontResolver fontResolver = renderer.getFontResolver();//避免中文為空設置系統字體fontResolver.addFont("static/fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);fontResolver.addFont("static/fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);String url = "File:///" + path + "/";log.info("導出 文件地址  = {}", url);//根據參數集個數循環調用模板,追加到同一個pdf文檔中//(注意:此處從1開始,因為第0是創建pdf,從1往后則向pdf中追加內容)for (int i = 0; i < listVars.size(); i++) {Document docAppend = generateDoc(templateEngine, templateName, listVars.get(i));renderer.setDocument(docAppend, null);renderer.getSharedContext().setBaseURL(url);//展現和輸出pdfrenderer.layout();if (i == 0) {renderer.createPDF(out, false);} else {//寫下一個pdf頁面renderer.writeNextDocument();}}renderer.finishPDF(); //完成pdf寫入}/*** pdf下載** @param templateEngine 配置* @param templateName   模板名稱(帶后綴.ftl)* @param listVars       模板參數集* @param response       HttpServletResponse* @param fileName       下載文件名稱(帶文件擴展名后綴)*/public static void download(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName) {// 設置編碼、文件ContentType類型、文件頭、下載文件名response.setCharacterEncoding("utf-8");response.setContentType("multipart/form-data");try {response.setHeader("Content-Disposition", "attachment;fileName=" +new String(fileName.getBytes("gb2312"), "ISO8859-1"));} catch (UnsupportedEncodingException e) {log.error(e.getMessage(), e);}try (ServletOutputStream out = response.getOutputStream()) {generateAll(templateEngine, templateName, out, listVars);out.flush();} catch (Exception e) {log.error(e.getMessage(), e);}}public static void downloadWithImg(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName, String path) {// 設置編碼、文件ContentType類型、文件頭、下載文件名response.setCharacterEncoding("utf-8");response.setContentType("multipart/form-data");try {response.setHeader("Content-Disposition", "attachment;fileName=" +new String(fileName.getBytes("gb2312"), "ISO8859-1"));} catch (UnsupportedEncodingException e) {log.error(e.getMessage(), e);}try (ServletOutputStream out = response.getOutputStream()) {generateAll(templateEngine, templateName, out, listVars, path);out.flush();} catch (Exception e) {log.error(e.getMessage(), e);}}/*** pdf預覽** @param templateEngine 配置* @param templateName   模板名稱(帶后綴.ftl)* @param listVars       模板參數集* @param response       HttpServletResponse*/public static void preview(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response) {try (ServletOutputStream out = response.getOutputStream()) {generateAll(templateEngine, templateName, out, listVars);out.flush();} catch (Exception e) {log.error(e.getMessage(), e);}}public static void previewWithImg(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String path) {try (ServletOutputStream out = response.getOutputStream()) {generateAll(templateEngine, templateName, out, listVars, path);out.flush();} catch (Exception e) {log.error(e.getMessage(), e);}}/*** 生成pdf 指定文件絕對路徑** @param templateEngine 配置* @param templateName   模板名稱(帶后綴.ftl)* @param listVars       模板參數集* @param filePath       文件路徑*/public static void creatPdfFile(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, String filePath) {FileOutputStream out = null;try {//新建文件File file = new File(filePath);// 創建復制路徑File parent = file.getParentFile();if (!parent.exists()) {parent.mkdirs();}// 復制文件if (file.exists()) {file.createNewFile();}out = new FileOutputStream(file);generateAll(templateEngine, templateName, out, listVars);out.flush();} catch (Exception e) {log.error(e.getMessage(), e);} finally {if (null != out) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 多個PDF合并功能 要捕獲錯誤信息,所以不用try-catch** @param files    多個PDF的路徑* @param savePath 生成的新PDF絕對路徑*/@SneakyThrowspublic static void mergePdfFiles(String[] files, String savePath) {if (files.length > 0) {
//            try {com.lowagie.text.Document document = new com.lowagie.text.Document(new PdfReader(files[0]).getPageSize(1));PdfCopy copy = new PdfCopy(document, new FileOutputStream(savePath));document.open();for (String file : files) {PdfReader reader = new PdfReader(file);int n = reader.getNumberOfPages();for (int j = 1; j <= n; j++) {document.newPage();PdfImportedPage page = copy.getImportedPage(reader, j);copy.addPage(page);}}document.close();
//            } catch (IOException | DocumentException e) {
//                e.printStackTrace();
//            }}}}

pdf.html 模版

多頁模版

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml" layout:decorator="layout">
<head lang="en"><title></title><style>@media print {div.footer-content {display: block;font-family: SimSun;font-size: 16px;color: #000;text-align: left;margin-left: -490px;position: running(footer-content);}}@page {size: A4; /*設置紙張大小:A4(210mm 297mm)、A3(297mm 420mm) 橫向則反過來*/margin: 1in;padding: 1em;}.page {page-break-after: always;font-size: 16px;font-family: SimSun;}/* 防止最后一頁多空頁 */.page:last-child {page-break-after: auto;}body {font-family: 'SimSun'}h1 {text-align: center;line-height: 60px;}table, th, td {border: 1px solid black;border-collapse: collapse;padding: 3px;font-size: 16px;height: 10px;}.half-cell {position: relative;}.footer {position: fixed;bottom: 10px;left: 0;width: 100%;font-size: 12px;margin-left: 10px;margin-right: 10px; /* 右邊也留 */}.footer .right {text-align: right;margin-top: 30px;}</style>
</head>
<!--這樣配置不中文不會顯示-->
<!--<body style="font-family: 宋體">-->
<body style="font-family: 'SimSun'">
<!--第一頁-->
<div class="page"><div style="width: 100%; overflow: hidden;"><!----><div style="float: left; width: 40%;"><table style="width: 100%; border-collapse: collapse;text-align: center;"><colgroup><col style="width: 50%;" /><col style="width: 50%;" /></colgroup><tr class="expertTitle"><td>**</td><td th:text="${bjbh}"></td></tr><tr class="expertTitle"><td>**</td><td th:text="${bdh}"></td></tr><tr><td>**</td><td th:text="${fbType}"></td></tr></table></div><!----><div style="float: right; width: 30%; text-align: right;"><img th:src="${photo}" alt="二維碼" style="width: 100px; height: 100px;"/></div></div><h2 style="text-align: center;" data-pdf-bookmark-name="***">***</h2><div style="line-height: 20px;"><!-- 繼續下面內容 --><div style="margin-top: 20px; font-size: 16px; margin-left: 10px; line-height: 1.8;"><span style="border-bottom: 1px solid #000; padding: 0 0px" th:text="${name}"></span> <span></span><p style="text-indent: 2em;">***</p></div><div><table style="text-align: center; width: 100%; border-collapse: collapse;" border="1"><colgroup><!-- 設置表格寬度 --><col style="width: 16.66%;" /><col style="width: 16.66%;" /><col style="width: 16.66%;" /><col style="width: 16.66%;" /><col style="width: 16.66%;" /><col style="width: 16.66%;" /></colgroup><tr><td>**</td><!-- 設置表格合并 --><td colspan="5" th:text="${jsdd}"></td></tr><tr style="height: 150px;"><td colspan="1">**</td><!-- 設置表格自動換行 --><td colspan="3" style="text-align: left; vertical-align: top; padding: 5px;"><span th:text="${zbdljg}"></span></td></tr></table></div></div><div class="footer" ><div>附注:</div><div>&#160;1. ****</div><div class="right"><div>******&#160;&#160;</div><div>2020&#160;&#160;</div></div></div></div><div class="page"><div style="width: 100%; overflow: hidden;"><!----><div style="float: left; width: 40%;text-align: center;"><table style="width: 100%; border-collapse: collapse;text-align: center;"><colgroup><col style="width: 50%;" /><col style="width: 50%;" /></colgroup><tr class="expertTitle"><td>**</td><td th:text="${bjbh}"></td></tr><tr class="expertTitle"><td>**</td><td th:text="${bdh}"></td></tr><tr><td>**</td><td th:text="${fbType}"></td></tr></table></div><!----><div style="float: right; width: 30%; text-align: right;"><img th:src="${photo}" alt="二維碼" style="width: 100px; height: 100px;"/></div></div><h2 style="text-align: center;" data-pdf-bookmark-name="專業工程暫估價明細表">專業工程暫估價明細表</h2><table style="width: 100%; border-collapse: collapse; font-weight: normal;text-align: center;" ><colgroup><col style="width: 20%;" /><col style="width: 20%;" /><col style="width: 20%;" /><col style="width: 20%;" /><col style="width: 20%;" /></colgroup><tr><td>序號</td><td>專業工程名稱</td><td>專業類別</td><td>招標主體</td><td>金額(萬元)</td></tr><!-- 循環 --><tr th:each="item, iterStat : ${list}" ><td th:text="${iterStat.count}"></td><td th:text="${item.projectName}"></td><td th:text="${item.projectType}"></td><td th:text="${item.bidEntity}"></td><td th:text="${item.amount}"></td></tr></table><div class="footer"><div>附注:</div><div>1. 本中標可通過二維碼在上海市建筑業官方微信驗證。</div><div class="right" style=" margin-right: 30px;"><div> ***&#160;&#160;</div><div>2020&#160;&#160;</div></div></div></div></body>
</html>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91129.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91129.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91129.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Qt 遠程過程調用(RPC)實現方案

在分布式系統開發中&#xff0c;遠程過程調用&#xff08;RPC&#xff09;是實現跨進程、跨機器通信的重要技術。Qt 作為一個強大的跨平臺框架&#xff0c;提供了多種 RPC 實現方案&#xff0c;能夠滿足不同場景下的通信需求。本文將深入探討 Qt 中 RPC 的各種實現方式&#xf…

攻防世界-引導-Web_php_unserialize

題目內容&#xff1a;出現一段源代碼&#xff0c;分段分析第一部分如下<?php class Demo { private $file index.php;public function __construct($file) { $this->file $file; }function __destruct() { echo highlight_file($this->file, true); }function __w…

pytorch學習筆記-自定義卷積

未完結的草稿———&#xff01;大概是準備整合一下常見的層&#xff0c;整合完感覺就可以進行搭建了&#xff08;還沒進行到這一步所以不太確定版&#xff09; &#xff08;ps我將在完結這一篇的時候刪除上面的小字and二編一下整篇文章的結構&#xff0c;如果看到了這部分文字…

[明道云]-基礎教學2-工作表字段 vs 控件:選哪種?

本文深入解析“工作表字段”與“控件”的關系與差別,并從結構、功能、使用場景和選型建議等方面進行對比。 一、基礎概念厘清 ? 工作表字段 = 數據模型中的列 工作表字段相當于數據庫表中的列,是記錄每條業務對象(如訂單、客戶等)屬性的數據項,每個字段都有明確的名稱和…

C++-一篇文章入門coroutines協程

文章目錄前言什么是協程協程實現原理C協程的最小例子12345協程等效代碼協程傳值的例子前言 最近學習了一下C協程&#xff0c;這篇文章將介紹協程的相關概念&#xff0c;以及在C中如何使用協程。 什么是協程 C中&#xff0c;協程&#xff08;coroutines&#xff09;可以理解為…

數字經濟專業的就業全景指南

CDA數據分析師證書含金量高&#xff0c;適應了未來數字化經濟和AI發展趨勢&#xff0c;難度不高&#xff0c;行業認可度高&#xff0c;對于找工作很有幫助。一、數字經濟就業熱力圖二、核心崗位發展路徑1. 互聯網數字運營崗2. 金融科技崗崗位類型技能組合證書加持5年薪資范圍智…

PDF轉Word免費工具!批量處理PDF壓縮,合并, OCR識別, 去水印, 簽名等全功能詳解

大家好&#xff0c;歡迎來到程序視點&#xff01;我是你們的老朋友.小二&#xff01;前言PDF軟件我發的非常多&#xff0c;但今天這款工具是大家公認最值得推薦的&#xff0c;這款軟件就是PDF24PDF24幾乎包含了PDF的所有功能&#xff0c;目前是更新到了最新版本&#xff01;文末…

Flutter開發實戰之Widget體系與布局原理

第3章:Widget體系與布局原理 在前面兩章中,我們已經搭建好了Flutter開發環境,并且了解了Dart語言的基礎知識。現在是時候深入Flutter的核心——Widget體系了。如果說Dart是Flutter的語言基礎,那么Widget就是Flutter的靈魂。理解Widget體系,是掌握Flutter開發的關鍵所在。…

C++:stack與queue的使用

stack與queue的使用一.stack與queuej基礎1.stack1.1基本認識1.2示例代碼代碼功能解析2.queue2.1基礎知識操作說明2.2示例代碼代碼分析 一.stack與queuej基礎 1.stack 1.1基本認識以上圖片展示了棧&#xff08;stack&#xff09;這種數據結構的基本操作示意。棧是一種遵循后進先…

Unity 編輯器開發 之 Excel導表工具

一個簡單的Excel導表工具&#xff0c;可以用來熱更數據配置工具使用&#xff1a;&#xfeff;&#xfeff;執行菜單 SDGSupporter/Excel/1.Excel2Cs 生成c#腳本。&#xfeff;&#xfeff;等待C#類編譯完成&#xfeff;&#xfeff;執行菜單 SDGSupporter/Excel/2.Excel2Bytes …

【數據結構與算法】力扣 415. 字符串相加

題目描述 415. 字符串相加 給定兩個字符串形式的非負整數 num1 和num2 &#xff0c;計算它們的和并同樣以字符串形式返回。 你不能使用任何內建的用于處理大整數的庫&#xff08;比如 BigInteger&#xff09;&#xff0c; 也不能直接將輸入的字符串轉換為整數形式。 示例 1…

進階向:Manus AI與多語言手寫識別

Manus AI與多語言手寫識別:從零開始理解 手寫識別技術作為人工智能領域的重要應用之一,近年來在智能設備、教育、金融等行業得到了廣泛運用。根據市場調研機構IDC的數據顯示,2022年全球手寫識別市場規模已達到45億美元,預計到2025年將突破70億美元。其中,多語言手寫識別技…

Javaweb————HTTP請求頭屬性講解

??????????????????????前面我們已經說過http請求分為三部分&#xff0c;請求行&#xff0c;請求頭和請求體 請求頭包含若干個屬性&#xff1a;格式為屬性名&#xff1a;屬性值&#xff0c;這篇文章我們就來介紹一下http請求頭中一些常見屬性的含義 我們…

9.c語言常用算法

查找順序查找&#xff08;線性查找&#xff09;算法思想&#xff1a;從數組的第一個元素開始&#xff0c;逐個與目標值進行比較&#xff0c;直到找到目標值或查找完整個數組。時間復雜度&#xff1a;最好情況&#xff1a;O(1)&#xff08;目標在第一個位置&#xff09;最壞情況…

AI小智源碼分析——音頻部分(一)

一、源碼跳轉這里采用了函數重載來進行代碼復用&#xff0c;當需要對I2S接口的數據進行配置&#xff0c;比如左右音道切換&#xff0c;可以使用第二個構造函數&#xff0c;這里小智使用的是第一個構造函數&#xff0c;即只傳遞I2S相關的引腳參數&#xff08;不帶slot mask&…

【GNSS原理】【LAMBDA】Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法[2025年7月]

Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法 作者&#xff1a;齊花Guyc(CAUC) 文章目錄Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法一.整周模糊度理論1.LAMBDA算法干了一件什么事情&#xff1f;2.LAMBDA算法步驟&#xff08;1&#xff09;去相關&#xff08;Z變換…

計算機畢業設計java在線二手系統的設計與實現 基于Java的在線二手交易平臺開發 Java技術驅動的二手物品管理系統

計算機畢業設計java在線二手系統的設計與實現z2n189&#xff08;配套有源碼 程序 mysql數據庫 論文&#xff09; 本套源碼可以在文本聯xi,先看具體系統功能演示視頻領取&#xff0c;可分享源碼參考。隨著互聯網技術的飛速發展&#xff0c;二手交易市場也逐漸從傳統的線下模式轉…

如何進行項目復盤?核心要點分析

進行項目復盤需要明確復盤目標、確定復盤參與人員、選擇合適的復盤方法、梳理項目過程與關鍵節點、分析成功與失敗的原因、總結經驗教訓并制定改進計劃。其中&#xff0c;選擇合適的復盤方法尤其關鍵&#xff0c;常見的復盤方法包括魚骨圖分析法、SWOT分析法、PDCA循環法&#…

LeetCode 923.多重三數之和

給定一個整數數組 arr &#xff0c;以及一個整數 target 作為目標值&#xff0c;返回滿足 i < j < k 且 arr[i] arr[j] arr[k] target 的元組 i, j, k 的數量。 由于結果會非常大&#xff0c;請返回 109 7 的模。 示例 1&#xff1a; 輸入&#xff1a;arr [1,1,2,2,…

.Net日志系統Logging-五

日志概念 日志級別 NET (Microsoft.Extensions.Logging) 中定義的 6 個標準日志級別&#xff0c;按嚴重性從低到高排列&#xff1a; 日志級別數值描述典型使用場景Trace0最詳細的信息&#xff0c;包含敏感數據&#xff08;如請求體、密碼哈希等&#xff09;。僅在開發或深度故…