SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印
更新時間:2024年03月26日 10:06:45 作者:從烏托邦醒來
這篇文章主要為大家詳細介紹了SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印的相關(guān)知識,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
一、依賴
<!-- itext7 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.2.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>io</artifactId>
<version>7.2.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>forms</artifactId>
<version>7.2.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>layout</artifactId>
<version>7.2.4</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>svg</artifactId>
<version>7.2.4</version>
</dependency>
<!-- font-asian 用于itext使用東亞字體 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>font-asian</artifactId>
<version>7.2.4</version>
</dependency>
<!-- html2pdf 用于轉(zhuǎn)換html為pdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>4.0.1</version>
</dependency>二、代碼
2.1、通用方法
/**
*
* @param pdf_path PDF文件保存路徑
* @param pdf_name PDF文件名
* @param cookie 用戶信息
* @param waterContent 水印內(nèi)容
* @return
* @throws AppException
*/
public PmsLXPDF createHtmlToPDF(String pdf_path, String pdf_name, String cookie, String waterContent) throws AppException {
logger.info("開始createPDF");
PmsLXPDF pdf = new PmsLXPDF();
String pdfViewUrl = "這里是接口路徑,生成jsp模板";
String url = Config.getMeurl();
logger.info("createPDF方法獲取meurl[{}]",url);
ItextHtmlToPdf.convert(url + pdfViewUrl, pdf_path, cookie, waterContent);
File file = new File(pdf_path);
if (file == null || !file.exists()) {
throw new AppException("立項報告生成pdf失??!");
}
try {
//可以做一些保存的操作
} catch (Exception e) {
logger.error("Exception happened", e);
}
//pdf.setSuccess(StringUtils.isNotBlank(id));
logger.info("createPDF結(jié)束");
return pdf;
}
/**
* html轉(zhuǎn)pdf
*
* @param srcPath html路徑,可以是硬盤上的路徑,也可以是網(wǎng)絡(luò)路徑
* @param destPath pdf保存路徑
* @return 轉(zhuǎn)換成功返回true
*/
public static boolean convert(String srcPath, String destPath, String cookie, String waterContent) {
if (!FileUtils.createFile(destPath)) {
LOGGER.error("目標(biāo)路徑PDF文件已存在,生成PDF失?。? + destPath);
return false;
}
LOGGER.info("convert方法傳入的srcPath[{}],cookie[{}]",srcPath,cookie);
InputStream in = null;
OutputStream out = null;
HttpURLConnection connection = null;
try {
if (srcPath.startsWith("http")) {
LOGGER.info("convert方法傳入的srcPath[{}],進入了需要cookie的分支",srcPath);
URL url = new URL(srcPath);
int connectTimeout = Integer.parseInt(ConfigUtil.getConfig("xxx", "30000"));
int readTimeout = Integer.parseInt(ConfigUtil.getConfig("xxx", "30000"));
connection = (HttpURLConnection) url.openConnection();
// 設(shè)置連接主機服務(wù)器的超時時間
connection.setConnectTimeout(connectTimeout);
// 設(shè)置讀取遠程返回的數(shù)據(jù)時間
connection.setReadTimeout(readTimeout);
if (StringUtils.isNotBlank(cookie)) {
// 設(shè)置Cookie
String cookies = "SESSION=".concat(cookie);
connection.setRequestProperty("Cookie", cookies);
}
// 連接和獲取響應(yīng)
connection.connect();
in = connection.getInputStream();
} else {
LOGGER.info("convert方法傳入的srcPath[{}],進入了不需要登錄的分支",srcPath);
in = new FileInputStream(srcPath);
}
out = new ByteArrayOutputStream();
HtmlPrittifier.fixHtml(in, out);
} catch (Throwable t) {
LOGGER.error("讀取該文件失?。? + srcPath, t);
return false;
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
if (connection != null) {
connection.disconnect();// 關(guān)閉遠程連接
}
}
String html = out.toString();
converCreatPdf(destPath, cookie, html, waterContent);
return true;
}
private static boolean converCreatPdf(String destPath, String cookie, String html, String waterContent) {
try (OutputStream fos = new FileOutputStream(destPath)) {
//將html轉(zhuǎn)換成pdf
PdfWriter pdfWriter = new PdfWriter(fos);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
// 統(tǒng)一設(shè)置頁眉
String header = "我是頁眉";
Header headerHandler = new Header(header);
pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
// 統(tǒng)一設(shè)置頁腳
Footer footerHandler = new Footer();
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
if (StringUtils.isNotBlank(waterContent)) {
// 添加水印
PdfWaterMarker pdfWaterMarker = new PdfWaterMarker(waterContent);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pdfWaterMarker);
}
// 需要先生成document,而不是直接生成pdf文件(因直接生成pdf文件會關(guān)閉流)
ConverterProperties converterProperties = getConverterProperties(cookie);
Document document = HtmlConverter.convertToDocument(html, pdfDoc, converterProperties);
// flush觸發(fā)寫操作,此時才會觸發(fā)已經(jīng)注冊的事件處理器
document.flush();
// 待document對象寫完后,才能開始寫入總頁碼
LOGGER.debug("PDF總頁數(shù):" + document.getPdfDocument().getNumberOfPages());
footerHandler.writeTotal(pdfDoc);
document.close();
} catch (Throwable t) {
LOGGER.error("生成PDF失敗:" + destPath, t);
return false;
}
return true;
}
public static ConverterProperties getConverterProperties(String cookie) {
ConverterProperties converterProperties = new ConverterProperties();
FontProvider fontProvider = getFontProvider();
converterProperties.setFontProvider(fontProvider);
CookieResourceRetriever myResourceRetriever = new CookieResourceRetriever(cookie);
converterProperties.setResourceRetriever(myResourceRetriever);
return converterProperties;
}
public void writeTotal(PdfDocument pdf) {
Canvas canvas = new Canvas(placeholder, pdf);
canvas.setFontSize(8);
if (font != null) {
// 設(shè)置支持中文
canvas.setFont(this.font);
// 在占位符寫入總頁數(shù)
canvas.showTextAligned(String.format("/共[%d]頁", pdf.getNumberOfPages()), 0, descent,
TextAlignment.LEFT);
}
canvas.close();
}2.2、工具類
public class HtmlPrittifier {
/**
* 將HTML標(biāo)準(zhǔn)化,補全缺失的閉標(biāo)簽
*
* @param in
* @param out
*/
public static void fixHtml(InputStream in, OutputStream out) {
// obtain a new Tidy instance
Tidy tidy = new Tidy();
// set desired config options using tidy setters
tidy.setXHTML(true);
tidy.setInputEncoding("utf8");
tidy.setShowWarnings(true);
tidy.setWraplen(1024);
tidy.setSmartIndent(true);
tidy.setQuiet(true);
tidy.setPrintBodyOnly(false);
tidy.setOutputEncoding("utf8");
tidy.setTidyMark(false);
// output document even if errors were found. 防止有些自定義tag匹配不上導(dǎo)致pdf不輸出
tidy.setForceOutput(true);
// 不轉(zhuǎn)換uri,防止uri中有中文,會將非ascii碼轉(zhuǎn)為十六進制,導(dǎo)致找不到中文圖片鏈接。
tidy.setFixUri(false);
tidy.parse(in, out);
}
/**
* 將HTML標(biāo)準(zhǔn)化,補全缺失的閉標(biāo)簽
*
* @param htmlString
*/
@SneakyThrows
public static String fixHtml(String htmlString) {
ByteArrayInputStream in = new ByteArrayInputStream(htmlString.getBytes("UTF-8"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
fixHtml(in, out);
return out.toString();
}
}2.3、水印實現(xiàn)方法
private static PdfFont createDefaultFont() throws IOException {
return PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
}
protected static class PdfWaterMarker implements IEventHandler {
private PdfFont font;
private String waterContent;
public PdfWaterMarker(String waterContent) {
this.waterContent = waterContent;
try {
this.font = createDefaultFont();
} catch (IOException e) {
LOGGER.error("PDF Header設(shè)置中文字體失敗", e);
}
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdfDoc);
Canvas canvas = new Canvas(pdfCanvas, pageSize);
// 設(shè)置水印文字內(nèi)容
Paragraph waterMarker = new Paragraph(waterContent)
.setFont(font)
.setOpacity(0.15f)// 設(shè)置透明度
.setFontSize(16);// 文字大小
/*for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
canvas.showTextAligned(waterMarker, (150 + i * 300), (160 + j * 150), pdfDoc.getNumberOfPages(),
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0.3f);
}
}*/
// 計算水印的起始位置和間隔
float xStart = 1;
float yStart = 1;
float stepX = 150;
float stepY = 150;
// 在頁面上循環(huán)繪制水印文字
for (float x = xStart; x < pageSize.getWidth(); x += stepX) {
for (float y = yStart; y < pageSize.getHeight(); y += stepY) {
// 繪制水印文字
canvas.showTextAligned(waterMarker, x, y, pdfDoc.getNumberOfPages(),
TextAlignment.LEFT, VerticalAlignment.MIDDLE, 0.6f);
}
}
// 關(guān)閉流
canvas.close();
}
}2.4、頁眉實現(xiàn)方法
private static PdfFont createDefaultFont() throws IOException {
return PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
}
protected static class Header implements IEventHandler {
private String header;
private PdfFont font;
public Header(String header) {
this.header = header;
try {
this.font = createDefaultFont();
} catch (IOException e) {
LOGGER.error("PDF Header設(shè)置中文字體失敗", e);
}
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent)event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
Document doc = new Document(pdf);
float leftMargin = doc.getLeftMargin();
float rightMargin = doc.getRightMargin();
float bottomMargin = doc.getBottomMargin();
float topMargin = doc.getTopMargin();
float height = pageSize.getHeight();
float width = pageSize.getWidth();
Canvas canvas = new Canvas(new PdfCanvas(page), pageSize);
if (font != null) {
// 設(shè)置支持中文
canvas.setFont(this.font);
}
canvas.setFontSize(8);
// Creates drawing canvas
PdfCanvas pdfCanvas = new PdfCanvas(page);
pdfCanvas.setLineWidth(0.1f);
pdfCanvas.moveTo(leftMargin, height - topMargin + 3).lineTo(width - rightMargin, height - topMargin + 3)
.stroke();
// Write text at position
canvas.showTextAligned(header, width / 2, height - 30, TextAlignment.CENTER);
canvas.close();
}
}2.5、頁腳實現(xiàn)方法
protected static class Footer implements IEventHandler {
// 寫總頁碼的占位符
protected PdfFormXObject placeholder;
// 頁腳占位符大小
private float side = 36;
// 頁腳占位符位置向右調(diào)整移動1,向下調(diào)整移動3
private float space = 1;
private float descent = 3;
private PdfFont font;
public Footer() {
this.placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));
try {
this.font = createDefaultFont();
} catch (IOException e) {
LOGGER.error("PDF Footer設(shè)置中文字體失敗", e);
}
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent)event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
int pageNumber = pdf.getPageNumber(page);
Rectangle pageSize = page.getPageSize();
LOGGER.debug(String.format("當(dāng)前處理第[%d]頁", pageNumber));
Document doc = new Document(pdf);
float leftMargin = doc.getLeftMargin();
float rightMargin = doc.getRightMargin();
float bottomMargin = doc.getBottomMargin();
float height = page.getPageSize().getHeight();
float width = page.getPageSize().getWidth();
// 頁腳的位置
float x = width / 2;
float y = bottomMargin / 2;
// Creates drawing canvas
PdfCanvas pdfCanvas = new PdfCanvas(page);
Canvas canvas = new Canvas(pdfCanvas, pageSize);
if (font != null) {
// 設(shè)置支持中文
canvas.setFont(this.font);
}
canvas.setFontSize(8);
// 設(shè)置支持橫線
// canvas.setStrokeWidth(0.1f);
pdfCanvas.setLineWidth(0.1f);
pdfCanvas.moveTo(leftMargin, bottomMargin - 3).lineTo(width - rightMargin, bottomMargin - 3).stroke();
// 設(shè)置支持頁碼
Paragraph p = new Paragraph().add(String.format("第[%d]頁", pageNumber));
canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);
canvas.close();
// 添加占位符,用于寫入總頁碼
pdfCanvas.addXObjectAt(placeholder, x + space, y - descent);
pdfCanvas.release();
}三、jsp實現(xiàn)水印
不影響之前已經(jīng)生成模板的代碼
<style>
#watermark div {
/* color: rgba(255, 0, 0, .1); */
color: rgba(0, 0, 255, .1);
position: absolute;
font-size: 24px;
white-space: nowrap;
transform: rotate(-30deg);
-ms-transform: rotate(-30deg); /* IE 9 */
-moz-transform: rotate(-30deg); /* Firefox */
-webkit-transform: rotate(-30deg); /* Safari 和 Chrome */
-o-transform: rotate(-30deg); /* Opera */
}
</style>
<div id="watermark"
style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: transparent; pointer-events: none; z-index: 99999999;">
</div>
<script>
var operatorName = '${requestScope.xx.xxx}';
var erpId = '${requestScope.xx.xxx}';
(function() {
var w = screen.width;
var h = screen.height;
var r = Math.sqrt(w * w + h * h);
var i = 0;
var j = 0;
var watermark = document.querySelector('#watermark');
var div;
var top, left;
for (var i = 0; i < 10; ++i) {
for (var j = 0; j < 10; ++j) {
div = document.createElement('div');
div.innerText = '禁止外傳:' + operatorName + '(' + erpId + ')';
top = i * 100 + 'px';
left = 500 * j - 50 - (i % 2 == 0 ? 250 : 0) + 'px';
div.style.setProperty('top', top);
div.style.setProperty('left', left);
watermark.appendChild(div);
}
}
})();
</script>以上就是SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印的詳細內(nèi)容,更多關(guān)于SpringBoot HTML轉(zhuǎn)PDF的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java+MySQL實現(xiàn)學(xué)生信息管理系統(tǒng)源碼
這篇文章主要為大家詳細介紹了Java+MySQL實現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
Java注解之超越Javadoc的元數(shù)據(jù)利器詳解
本文將深入探討 Java 注解的定義、類型、內(nèi)置注解、自定義注解、保留策略、實際應(yīng)用場景及最佳實踐,無論是初學(xué)者還是資深開發(fā)者,都能通過本文了解如何利用注解提升代碼質(zhì)量和開發(fā)效率2025-05-05
springboot rabbitmq整合rabbitmq之消息持久化存儲問題
這篇文章主要介紹了springboot rabbitmq整合rabbitmq之消息持久化存儲問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
如何在mybatis中向BLOB字段批量插入數(shù)據(jù)
這篇文章主要介紹了如何在mybatis中向BLOB字段批量插入數(shù)據(jù)的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-10-10
SpringBoot整合RestTemplate用法的實現(xiàn)
本篇主要介紹了RestTemplate中的GET,POST,PUT,DELETE、文件上傳和文件下載6大常用的功能,具有一定的參考價值,感興趣的可以了解一下2023-08-08

