Java實(shí)現(xiàn)將列表數(shù)據(jù)導(dǎo)出為PDF文件并添加水印
一、實(shí)現(xiàn)效果

二、遇到的問(wèn)題
1.實(shí)現(xiàn)導(dǎo)出PDF主體代碼參考:Java純代碼實(shí)現(xiàn)導(dǎo)出PDF功能,下圖是原作者實(shí)現(xiàn)的效果

2.導(dǎo)出報(bào)錯(cuò)Font 'STSong-Light' with 'UniGB-UCS2-H' is not recognized.。參考文末補(bǔ)充內(nèi)容
網(wǎng)上都是說(shuō)jar包的版本不對(duì),導(dǎo)致的字體兼容性問(wèn)題。換了jar包版本發(fā)現(xiàn)沒(méi)效果,后來(lái)索性直接把字體下載到本地直接引入。
3.jar包發(fā)布到服務(wù)器上導(dǎo)出PDF的時(shí)候發(fā)生報(bào)錯(cuò)BOOT-INF/classes!/fonts/SimSun.ttf not exists。
可以看到字體文件在jar目錄下面是有的,但是發(fā)現(xiàn)classes后面多了個(gè)嘆號(hào)。這是引入外部字體方式不對(duì),后改用問(wèn)題2參考文章的第三種寫(xiě)法就沒(méi)問(wèn)題了。
添加水印參考:itextpdf5.5.13給pdf添加圖片水印、添加文字水?。ㄆ戒仯?、添加文字水印(單個(gè))、添加頁(yè)眉、頁(yè)腳、頁(yè)眉事件、添加圖片
三、測(cè)試數(shù)據(jù)展示
list:子節(jié)點(diǎn)數(shù)據(jù)
0 = {BasBudgetDetailVo@16046} "BasBudgetDetailVo(budgetId=2064535550, functionId=231231232, budgetQuantity=3, totalPrice=2664.00, functionName=功能1, functionDescription=功能1描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=1)"
1 = {BasBudgetDetailVo@16047} "BasBudgetDetailVo(budgetId=2039369726, functionId=231236478, budgetQuantity=1, totalPrice=888.00, functionName=功能1, functionDescription=功能1描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=1)"
2 = {BasBudgetDetailVo@16048} "BasBudgetDetailVo(budgetId=2039369725, functionId=231236473, budgetQuantity=1, totalPrice=888.00, functionName=功能2, functionDescription=功能2描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=2)"
3 = {BasBudgetDetailVo@16049} "BasBudgetDetailVo(budgetId=2056146943, functionId=231231241, budgetQuantity=1, totalPrice=888.00, functionName=功能2, functionDescription=功能2描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=2)"
4 = {BasBudgetDetailVo@16050} "BasBudgetDetailVo(budgetId=2047758334, functionId=231236487, budgetQuantity=1, totalPrice=888.00, functionName=功能3, functionDescription=功能3描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=3)"
5 = {BasBudgetDetailVo@16051} "BasBudgetDetailVo(budgetId=2039369724, functionId=231231245, budgetQuantity=1, totalPrice=888.00, functionName=功能3, functionDescription=功能3描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=3)"
6 = {BasBudgetDetailVo@16052} "BasBudgetDetailVo(budgetId=2047758333, functionId=231231597, budgetQuantity=1, totalPrice=888.00, functionName=功能4, functionDescription=功能4描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=4)"
7 = {BasBudgetDetailVo@16053} "BasBudgetDetailVo(budgetId=2030981118, functionId=231233154, budgetQuantity=1, totalPrice=888.00, functionName=功能4, functionDescription=功能4描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=4)"
8 = {BasBudgetDetailVo@16054} "BasBudgetDetailVo(budgetId=2030981117, functionId=231234596, budgetQuantity=1, totalPrice=888.00, functionName=功能5, functionDescription=功能5描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=5)"
9 = {BasBudgetDetailVo@16055} "BasBudgetDetailVo(budgetId=2030981116, functionId=231235487, budgetQuantity=1, totalPrice=888.00, functionName=功能6, functionDescription=功能6描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=6)"
functionInfoList:根節(jié)點(diǎn)數(shù)據(jù)
0 = {BasFunctionInfo@16090} "BasFunctionInfo(functionId=231234512, functionName=模塊1, functionDescription=, functionUnit=0, functionPrice=0.00, createName=管理員, createBy=admin, createTime=Wed Jan 24 16:56:35 CST 2024, updateName=管理員, updateBy=admin, updateTime=Wed Jan 24 16:56:38 CST 2024, functionQuantity=null, functionSort=1, parentId=null)"
1 = {BasFunctionInfo@16091} "BasFunctionInfo(functionId=231234879, functionName=模塊2, functionDescription=, functionUnit=0, functionPrice=0.00, createName=管理員, createBy=admin, createTime=Wed Jan 24 16:56:35 CST 2024, updateName=管理員, updateBy=admin, updateTime=Wed Jan 24 16:56:38 CST 2024, functionQuantity=null, functionSort=2, parentId=null)"
matchList:當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)數(shù)據(jù)
四、jar包引入
<!--導(dǎo)出pdf所需包--> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.10</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> </dependencies>
五、外部字體引入
字體文件資源自己百度,直接搜SimSun.ttf字體下載不難找

六、代碼實(shí)現(xiàn)
private final ResourceLoader resourceLoader;
public BasBudgetDetailServiceImpl(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* 導(dǎo)出pdf
*
* @param response
* @throws Exception
*/
@Override
public void downloadPdf(HttpServletResponse response) throws Exception {
// 業(yè)務(wù)數(shù)據(jù),根據(jù)需求查詢(xún)獲取
// 子節(jié)點(diǎn)數(shù)據(jù)
List<BasBudgetDetailVo> list;
// 根子節(jié)點(diǎn)數(shù)據(jù)
List<BasFunctionInfo> functionInfoList;
// 定義全局的字體靜態(tài)變量
Font content = null;
Resource resource = resourceLoader.getResource("classpath:/fonts/SimSun.ttf");
InputStream inputStream = resource.getInputStream();
BaseFont bfChinese = null;
try {
// 字體
bfChinese = BaseFont.createFont("SimSun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, IOUtils.toByteArray(inputStream), null);
content = new Font(bfChinese, 10, Font.NORMAL);
} catch (Exception e) {
e.printStackTrace();
}
BaseFont bf = null;
Font font = null;
try {
//創(chuàng)建字體
bf = BaseFont.createFont("SimSun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, IOUtils.toByteArray(inputStream), null);
//使用字體并給出顏色
font = new Font(bf, 20, Font.BOLD, BaseColor.BLACK);
} catch (Exception e) {
e.printStackTrace();
}
Document document = new Document(new RectangleReadOnly(842F, 595F));
try {
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
//打開(kāi)生成的pdf文件
document.open();
//設(shè)置標(biāo)題
Paragraph paragraph = new Paragraph("這是標(biāo)題文檔標(biāo)題", font);
paragraph.setAlignment(1);
//引用字體
document.add(paragraph);
// 總額
BigDecimal detailTotal = BigDecimal.valueOf(0);
for (BasFunctionInfo functionInfo : functionInfoList) {
// 匹配明細(xì)
List<BasBudgetDetailVo> matchList = list.stream().filter(item ->
String.valueOf(item.getParentId()).equals(String.valueOf(functionInfo.getFunctionId())))
.collect(Collectors.toList());
// 設(shè)置表格的列寬和列數(shù)
float[] widths = {10f, 35f, 70f, 10f, 10f, 20f, 20f};
PdfPTable table = new PdfPTable(widths);
table.setSpacingBefore(20f);
// 設(shè)置表格寬度為100%
table.setWidthPercentage(100.0F);
table.setHeaderRows(1);
table.getDefaultCell().setHorizontalAlignment(1);
//列表-表頭
String[] titleList = new String[]{"序號(hào)", "功能名稱(chēng)", "功能描述", "數(shù)量", "單位", "單價(jià)(元)", "總價(jià)(元)"};
addTableTitle(table, content, titleList);
// 模塊總額
BigDecimal modelTotal = BigDecimal.valueOf(0);
//列表數(shù)據(jù)
if (matchList.size() > 0) {
Integer index = 1;
for (BasBudgetDetailVo item : matchList) {
PdfPCell cell1 = new PdfPCell(new Paragraph(String.valueOf(index), content));
PdfPCell cell2 = new PdfPCell(new Paragraph(item.getFunctionName(), content));
PdfPCell cell3 = new PdfPCell(new Paragraph(item.getFunctionDescription(), content));
PdfPCell cell4 = new PdfPCell(new Paragraph(String.valueOf(item.getBudgetQuantity()), content));
PdfPCell cell5 = new PdfPCell(new Paragraph(item.getFunctionUnit(), content));
PdfPCell cell6 = new PdfPCell(new Paragraph(String.valueOf(item.getFunctionPrice()), content));
BigDecimal totalPrice = item.getFunctionPrice().multiply(BigDecimal.valueOf(item.getBudgetQuantity()));
PdfPCell cell7 = new PdfPCell(new Paragraph(String.valueOf(totalPrice), content));
//單元格對(duì)齊方式
cell1.setFixedHeight(20);
cell1.setHorizontalAlignment(Element.ALIGN_CENTER);
cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
// 文字長(zhǎng)度大于15的時(shí)候,設(shè)置表格行間距,底邊距離
if (item.getFunctionName().length() > 15) {
cell2.setLeading(0f, 1.5f);
cell2.setPaddingBottom(10);
}
cell2.setHorizontalAlignment(Element.ALIGN_CENTER);
cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
// 文字長(zhǎng)度大于30的時(shí)候,設(shè)置表格行間距,底邊距離
if (item.getFunctionDescription().length() > 30) {
cell3.setLeading(0f, 1.5f);
cell3.setPaddingBottom(10);
}
cell3.setHorizontalAlignment(Element.ALIGN_CENTER);
cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell4.setHorizontalAlignment(Element.ALIGN_CENTER);
cell4.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell5.setHorizontalAlignment(Element.ALIGN_CENTER);
cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell6.setHorizontalAlignment(Element.ALIGN_CENTER);
cell6.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell7.setHorizontalAlignment(Element.ALIGN_CENTER);
cell7.setVerticalAlignment(Element.ALIGN_MIDDLE);
table.addCell(cell1);
table.addCell(cell2);
table.addCell(cell3);
table.addCell(cell4);
table.addCell(cell5);
table.addCell(cell6);
table.addCell(cell7);
// 序號(hào)
index++;
modelTotal = modelTotal.add(totalPrice);
}
// 合計(jì)行
PdfPCell cell1 = new PdfPCell(new Paragraph("合計(jì)", content));
cell1.setFixedHeight(20);
cell1.setHorizontalAlignment(Element.ALIGN_CENTER);
cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
// 空格
PdfPCell cell2 = new PdfPCell(new Paragraph("", content));
cell2.setFixedHeight(20);
cell2.setHorizontalAlignment(Element.ALIGN_CENTER);
cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
// 數(shù)額
PdfPCell cell3 = new PdfPCell(new Paragraph(String.valueOf(modelTotal), content));
cell3.setFixedHeight(20);
cell3.setHorizontalAlignment(Element.ALIGN_CENTER);
cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);
table.addCell(cell1);
table.addCell(cell2);
table.addCell(cell2);
table.addCell(cell2);
table.addCell(cell2);
table.addCell(cell2);
table.addCell(cell3);
detailTotal = detailTotal.add(modelTotal);
}
document.add(new Paragraph("\n"));
document.add(new Paragraph("▋ " + functionInfo.getFunctionName(), content));
document.add(table);
document.add(new Paragraph("\n"));
if (matchList.size() == 0) {
document.add(new Paragraph("暫無(wú)數(shù)據(jù)", content));
}
}
document.add(new Paragraph("\n"));
document.add(new Paragraph("總計(jì):" + detailTotal + "元", content));
// 加水印
PdfContentByte waterMar = pdfWriter.getDirectContentUnder();
String text = "天天想辭職月月拿全勤";
addTextFullWaterMark(waterMar, text, bfChinese);
document.close();
} catch (DocumentException e) {
e.printStackTrace();
log.error("導(dǎo)出pdf失敗:{}", e);
}
}
/**
* 給表格添加表頭
*
* @param table
* @param content
* @param titleList
*/
public void addTableTitle(PdfPTable table, Font content, String[] titleList) {
PdfPCell cell = null;
for (String title : titleList) {
cell = new PdfPCell(new Paragraph(title, content));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setFixedHeight(20);
cell.setNoWrap(false);
table.addCell(cell);
}
}
/**
* 給pdf添加文字水?。ㄆ戒仯?
*
* @param waterMar
* @param text 水印文本
* @throws Exception
*/
public static void addTextFullWaterMark(PdfContentByte waterMar, String text, BaseFont bf) {
waterMar.beginText();
PdfGState gs = new PdfGState();
// 設(shè)置填充字體不透明度為0.2f
gs.setFillOpacity(0.2f);
waterMar.setFontAndSize(bf, 20);
// 設(shè)置透明度
waterMar.setGState(gs);
// 設(shè)置水印對(duì)齊方式 水印內(nèi)容 X坐標(biāo) Y坐標(biāo) 旋轉(zhuǎn)角度
for (int x = 0; x <= 900; x += 200) {
for (int y = -50; y <= 800; y += 200) {
waterMar.showTextAligned(Element.ALIGN_RIGHT, text, x, y, 35);
}
}
// 設(shè)置水印顏色
waterMar.setColorFill(BaseColor.GRAY);
//結(jié)束設(shè)置
waterMar.endText();
waterMar.stroke();
}
七、知識(shí)補(bǔ)充
itext 生成 PDF使用外部字體
在 Spring Boot 項(xiàng)目中,使用 iTextPDF 庫(kù)的 BaseFont.createFont() 方法并使用外部字體,您需要將字體文件放置在項(xiàng)目的資源文件夾中,然后使用相對(duì)路徑來(lái)引用它們。
1.絕對(duì)路徑
BaseFont.createFont("C:\\Windows\\Fonts\\simfang.ttf",BaseFont.IDENTITY_H,BaseFont.EMBEDDED)
2.將字體文件放置在 src/main/resources/fonts 目錄下,讀取resource目錄下的字體
BaseFont baseFont = BaseFont.createFont("classpath:/fonts/simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
注:此方式只有在打成jar時(shí)才生效
3.在 Spring Boot 項(xiàng)目中,您可以使用 ResourceLoader 來(lái)加載文件,而不必?fù)?dān)心文件路徑的問(wèn)題。ResourceLoader 可以在任何環(huán)境中工作,無(wú)論是在 IDEA 中運(yùn)行還是在打包的 JAR 文件中運(yùn)行。
@Service
public class MyService {
private final ResourceLoader resourceLoader;
public MyService(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void generatePdf() throws IOException, DocumentException {
//第一種情況 以 .ttf結(jié)尾的字體
Resource resource = resourceLoader.getResource("classpath:/fonts/simfang.ttf");
InputStream inputStream = resource.getInputStream();
// Use the input stream to create the BaseFont object
BaseFont baseFont = BaseFont.createFont("simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, inputStream, null);
//第二種情況 以 .ttc結(jié)尾的字體
Resource resource1 = resourceLoader.getResource("classpath:/fonts/simsun.ttc");
InputStream inputStream = resource1.getInputStream();
// Use the input stream to create the BaseFont object
BaseFont baseFont = BaseFont.createFont("simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, inputStream, null);
}
}
以上就是Java實(shí)現(xiàn)將列表數(shù)據(jù)導(dǎo)出為PDF文件并添加水印的詳細(xì)內(nèi)容,更多關(guān)于Java列表數(shù)據(jù)導(dǎo)出為PDF的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java編程中使用JDBC API連接數(shù)據(jù)庫(kù)和創(chuàng)建程序的方法
這篇文章主要介紹了Java編程中使用JDBC API連接數(shù)據(jù)庫(kù)和創(chuàng)建程序的基本教程,JDBC是一種用于執(zhí)行SQL語(yǔ)句的Java API,可以為多種關(guān)系數(shù)據(jù)庫(kù)提供統(tǒng)一訪問(wèn)需要的朋友可以參考下2015-12-12
Java Agent 動(dòng)態(tài)修改字節(jié)碼詳情
這篇文章主要介紹了Java Agent動(dòng)態(tài)修改字節(jié)碼的相關(guān)資料,需要的朋友可以參考下面文章具體的內(nèi)容2021-09-09
Java 基礎(chǔ)全面講解StringBuffer類(lèi)的使用
當(dāng)對(duì)字符串進(jìn)行修改的時(shí)候,需要使用 StringBuffer 和 StringBuilder類(lèi),和String類(lèi)不同的是,StringBuffer和 StringBuilder類(lèi)的對(duì)象能夠被多次的修改,并且不產(chǎn)生新的未使用對(duì)象2022-01-01
Java實(shí)現(xiàn)動(dòng)物換位游戲完整?過(guò)程詳解
大家好,今天嘗試用Java編程設(shè)計(jì)一個(gè)GUI界面的動(dòng)物換位游戲,游戲的結(jié)果是讓左右兩組的動(dòng)物交換位置,以下是具體設(shè)計(jì)過(guò)程,供大家參考2022-07-07
Springboot接口返回參數(shù)及入?yún)SA加密解密的過(guò)程詳解
這篇文章主要介紹了Springboot接口返回參數(shù)及入?yún)SA加密解密,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
spring?boot項(xiàng)目自定義參數(shù)校驗(yàn)規(guī)則示例詳解
這篇文章主要介紹了spring boot項(xiàng)目如何自定義參數(shù)校驗(yàn)規(guī)則,自定義校驗(yàn)規(guī)則和自帶的規(guī)則實(shí)現(xiàn)方式一樣,先自定義一個(gè)注解,然后指定校驗(yàn)類(lèi),在校驗(yàn)類(lèi)里實(shí)現(xiàn)具體的校驗(yàn)規(guī)則,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
spring?boot?mybatis日志輸出到控制臺(tái)的方法實(shí)踐
在開(kāi)發(fā)過(guò)程中我們往往需要打印出SQL語(yǔ)句,這樣就方便我們監(jiān)控問(wèn)題,本文主要介紹了spring?boot?mybatis日志輸出到控制臺(tái)的方法實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
SpringBoot自定義監(jiān)聽(tīng)器的項(xiàng)目實(shí)踐
Spring Boot提供了強(qiáng)大的事件模型,其中包括多種內(nèi)置監(jiān)聽(tīng)器,同時(shí)也支持開(kāi)發(fā)者自定義監(jiān)聽(tīng)器,下面就來(lái)介紹下SpringBoot自定義監(jiān)聽(tīng)器,感興趣的可以了解一下2024-03-03
Java多線程之定時(shí)器Timer的實(shí)現(xiàn)
定時(shí)/計(jì)劃功能在Java應(yīng)用的各個(gè)領(lǐng)域都使用得非常多,比方說(shuō)Web層面。本文主要為大家介紹了Java多線程中定時(shí)器Timer的實(shí)現(xiàn),感興趣的小伙伴可以了解一下2022-10-10

