java導出pdf文件
- 一、介紹
- 二、準備
- 三、實現效果
- 四、代碼
一、介紹
上一篇文章(java使用freemarker操作word(攜帶動態表格,圖片))https://blog.csdn.net/weixin_45853881/article/details/129298494
緊跟上文,現在不僅需要word,而且還要pdf
二、準備
導入依賴,這里使用iText 5沒有使用iText 7的,追求快點寫,至于iText 7,下次一定
<!-- iText 5核心庫-->
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.2.0</version>
</dependency>
<!--字體-->
<dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version>
</dependency>
三、實現效果
所有圖片均來源于網絡,兩個表格使用不同的方法進行寫的,水印透明度和顏色可以調節,水印頁碼也可以調整
剛加入了水印設置,懶得截圖了也,代碼里面有
四、代碼
這里把代碼都貼上來了,就把一下操作隱藏了,替換為了手敲,先上代碼,后展示樣式
package com.wy;import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.wy.tools.Staff;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;public class demo1 {public static void main(String[] args) {try {generatePDF();} catch (Exception e) {e.printStackTrace();}}// 生成 pdfpublic static void generatePDF() throws Exception{//臨時文件目錄String name = "檢查報告-" + UUID.randomUUID();FileOutputStream fos = new FileOutputStream("C:\\Users\\admin\\Downloads\\" + name + ".pdf");// 服務器對客戶端的HTTP請求的響應對象// HttpServletResponse response// ServletOutputStream os = response.getOutputStream();//新建文檔Document document = new Document();// 將內存中的 PDF 文檔內容轉換為二進制數據,處理流PdfWriter writer = PdfWriter.getInstance(document, fos);writer.setViewerPreferences(PdfWriter.PageModeUseThumbs);writer.setPageSize(PageSize.A4);// 注冊頁碼事件writer.setPageEvent(new PageNumberEvent());// 注冊水印事件writer.setPageEvent(new WatermarkEvent());// 打開文檔document.open();// 首頁part1(document,writer);// 總覽頁(表格1)document.newPage();part2(document);//內容第一頁(表格2)document.newPage();part3(document);// 查看頁碼設置生效了沒document.newPage();Paragraph con = new Paragraph(" ");document.add(con);// 關閉文檔document.close();}// 首頁 封面public static void part1(Document document, PdfWriter writer) throws Exception{// pdf首頁據上方默認無限邊界,所有先設置一個空的 好調整位置,不設置空的也可以,使用時間那里的絕對定位進行設置// 獲取頁面尺寸信息(A4默認寬595f,高842f,可根據實際調整)float pageWidth = document.getPageSize().getWidth();float pageHeight = document.getPageSize().getHeight();// 計算居中的坐標(x為頁面水平中心)float centerX = pageWidth / 2;// 空白占據Paragraph blank = new Paragraph(" "); //有空格,證明有內容,才可以進行設置間距blank.setSpacingBefore(10f); // 上方留出10磅的間距blank.setSpacingAfter(10f); // 下方留出10磅的間距document.add(blank);//logoParagraph logo = new Paragraph();Image image;try {image = Image.getInstance("https://img-home.csdnimg.cn/images/20201124032511.png");} catch (Exception e) {image = createBlankImage();}image.scaleToFit(100, 100); // 調整圖片大小// 方法作用是創建一個包含圖像的文本塊 最后一個參數:是否調整行高以適應圖像, 設置為ture之后上一步設置的scaleToFit需要等比設置才生效(例如image.scaleToFit(100, 100);會生效100,image.scaleToFit(200, 100);也會生效100)logo.add(new Chunk(image, 18, 0, true));Chunk textChunk = new Chunk("CSND", getFont(12, Font.BOLD));textChunk.setCharacterSpacing(-2f); //字符間距logo.add(textChunk);logo.setSpacingBefore(2f);logo.setIndentationLeft(19f); //左方縮進document.add(logo);//標題Paragraph title = new Paragraph();title.add(new Chunk("XXX用戶", getFont(28,Font.BOLD)));title.add(Chunk.NEWLINE);title.add(new Chunk("文章檢測報告", getFont(28,Font.BOLD)));title.setLeading(0, 2.8f);title.setAlignment(Element.ALIGN_CENTER);title.setSpacingBefore(49f); // 上方留出49磅的間距document.add(title);// 底部時間float bottomY = 130f;ColumnText ct = new ColumnText(writer.getDirectContent());Paragraph time = new Paragraph();time.add(new Chunk("官方檢測", getFont(18,Font.BOLD)));time.add(Chunk.NEWLINE);time.add(new Chunk("二〇二五年七月二十九日", getFont(18,Font.BOLD)));time.setLeading(0, 1.8f);time.setAlignment(Element.ALIGN_CENTER);ct.setSimpleColumn(centerX - 200f, //左側bottomY, //下方centerX + 200f, //右邊bottomY + 100f //上邊);ct.addElement(time);ct.go();}// 總覽頁public static void part2(Document document) throws Exception {Paragraph blank = new Paragraph(" ");blank.setSpacingAfter(10f);document.add(blank);Paragraph title = new Paragraph();title.add(new Chunk("xxx用戶", getFont(18, Font.BOLD)));title.add(new Chunk("檢測認證", getFont(18, Font.BOLD)));title.setAlignment(Element.ALIGN_CENTER);title.setSpacingAfter(10f);document.add(title);Paragraph content = new Paragraph();content.add(new Chunk("受xxx委托,xxx官方對xxx用戶信息進行審核。",getFont(16, Font.NORMAL)));content.setLeading(0, 2.4f); //設置段落的行間距// 設置首行縮進(約2個中文字符,假設16號字體下1個字符≈16磅)content.setFirstLineIndent(32f);content.setSpacingAfter(10f);document.add(content);// 創建一個2列的表格PdfPTable table = new PdfPTable(2);table.setWidthPercentage(90); // 可以減少寬度以避免邊緣擠壓,根據實際需要設置// 定義表格各行的單元格內容,按順序添加String[][] cellTexts = {{"檢查時間:2025.5.29"},{"檢查渠道:", "CSDN"}, // 第二行第二列將合并到第三行{"檢查官方:", ""}, // 第三行第二列留空(已被合并){"圖片1:",""}, // 合并單元格,使用單元格的值,在進行附加{"",""},};for (int i = 0; i < cellTexts.length; i++) {String[] rowTexts = cellTexts[i];for (int j = 0; j < rowTexts.length; j++) {String cellText = rowTexts[j];// 跳過已被合并的單元格 第三行第二列 第四行第二列if ((i == 2 || i == 3) && j == 1) {continue;}PdfPCell cell;// 處理需要插入圖片的單元格if (i == 3 || i == 4) { // 第4行和第5行try {Paragraph paragraph = new Paragraph();Image image;if (i == 3) { //第四行合并了paragraph.add(new Chunk(cellText, getFont(14, Font.NORMAL)));paragraph.add(Chunk.NEWLINE);try {image = Image.getInstance("https://pics2.baidu.com/feed/e824b899a9014c08c4754f93412bd5037bf4f412.jpeg");} catch (Exception e) {image = createBlankImage();}image.scaleToFit(100, 100); // 調整圖片大小paragraph.add(new Chunk(image, 0, 0, true));cell = new PdfPCell();cell.addElement(paragraph);} else { //第五行有兩個if (j == 0) {paragraph.add(new Chunk("圖片2:", getFont(14, Font.NORMAL)));paragraph.add(Chunk.NEWLINE);try {image = Image.getInstance("https://pics2.baidu.com/feed/7e3e6709c93d70cf8e1b7905b58c060bbba12bb5.jpeg");} catch (Exception e) {image = createBlankImage();}// 設置圖片屬性image.scaleToFit(100, 100); // 縮放圖片以適應單元格cell = new PdfPCell();cell.addElement(paragraph);paragraph.add(new Chunk(image, 0, 0, true));} else {try {image = Image.getInstance("https://pics6.baidu.com/feed/d833c895d143ad4b000f8b7dce528da4a50f06c2.jpeg");} catch (Exception e) {image = createBlankImage();}// 設置圖片屬性image.scaleToFit(100, 100); // 縮放圖片以適應單元格image.setAlignment(Image.ALIGN_CENTER); // 圖片居中// 創建包含圖片的單元格cell = new PdfPCell(image, true); // 第二個參數true表示拉伸圖片以填滿單元格}}} catch (Exception e) {// 圖片加載失敗時,使用文本替代cell = new PdfPCell(new Phrase("圖片加載失敗", getFont(14, Font.ITALIC)));e.printStackTrace();}} else {// 普通文本單元格cell = new PdfPCell(new Phrase(cellText, getFont(14, Font.NORMAL)));}// 第一行合并兩列if (i == 0) {cell.setColspan(2); // 合并兩列cell.setHorizontalAlignment(Element.ALIGN_LEFT); // 左對齊}// 第二行第二列:合并兩行if (i == 1 && j == 1) {cell.setRowspan(2); // 合并下面一行}if (i == 3) {cell.setColspan(2); // 合并兩列cell.setHorizontalAlignment(Element.ALIGN_LEFT); // 左對齊}// 設置單元格內邊距cell.setPadding(5);// 設置單元格內容垂直居中cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);}}// 將表格添加到文檔document.add(table);}// 內容頁public static void part3(Document document) throws Exception{Paragraph title = new Paragraph();title.add(new Chunk("情況匯總表", getFont(18,Font.BOLD)));title.setAlignment(Element.ALIGN_CENTER);title.setSpacingAfter(20f);document.add(title);// 創建一個5列的表格PdfPTable table = new PdfPTable(5);table.setWidthPercentage(100); // 可以減少寬度以避免邊緣擠壓,根據實際需要設置table.setHorizontalAlignment(Element.ALIGN_CENTER); //表格水平居中// 設置表格列寬比例float[] columnWidths = {1f, 4f, 3f,4f,3f};table.setWidths(columnWidths);// 添加表頭String[] headers = {"序號", "姓名", "年齡","職位","圖片"};for (String header : headers) {PdfPCell cell = new PdfPCell(new Phrase(header, getFont(12,null)));cell.setHorizontalAlignment(Element.ALIGN_CENTER); //表頭水平居中cell.setPadding(5);table.addCell(cell);}// 后續替換為接口對接查詢List<Staff> list = new ArrayList<>();list.add(new Staff("1","張三","30","軟件工程師","https://n.sinaimg.cn/sinakd20106/384/w2048h1536/20250517/64b2-bd7d404c74b6c162844f652af698d98e.jpg"));list.add(new Staff("2","李四","28","產品經理","https://img1.baidu.com/it/u=1520272912,1660814288&fm=253&fmt=auto&app=138&f=JPEG"));list.add(new Staff("3","王五","26","UI設計師","https://www.vipyl.com/user/webimg/2018123/20181231718374.jpg"));for (Staff staff : list) {addDataCell(table, staff.getId(), getFont(12,null));addDataCell(table, staff.getName(), getFont(12,null));addDataCell(table, staff.getAge(), getFont(12,null));addDataCell(table, staff.getWork(), getFont(12,null));Image image;try {image = Image.getInstance(staff.getPic());} catch (Exception e) {image = createBlankImage();}image.scaleToFit(100, 100);PdfPCell imageCell = new PdfPCell(image, true);imageCell.setHorizontalAlignment(Element.ALIGN_CENTER); // 圖片水平居中imageCell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 圖片垂直居中imageCell.setPadding(5);table.addCell(imageCell);}// 將表格添加到文檔document.add(table);}// 添加數據單元格的輔助方法private static void addDataCell(PdfPTable table, String data, Font font) {PdfPCell cell = new PdfPCell(new Phrase(data, font));cell.setHorizontalAlignment(Element.ALIGN_CENTER); // 水平居中cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中cell.setPadding(5);table.addCell(cell);}// 圖片獲取失敗處理 創建空白圖像private static Image createBlankImage() {try {BufferedImage blankImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);ByteArrayOutputStream baos = new ByteArrayOutputStream();ImageIO.write(blankImage, "PNG", baos);return Image.getInstance(baos.toByteArray());} catch (Exception e) {throw new RuntimeException("創建空白圖像失敗", e);}}// 字體設置public static Font getFont(Integer size,Integer type) throws Exception {if (size == null) size = 12;if (type == null) type = Font.NORMAL;BaseFont bf = BaseFont.createFont("STSong-Light", // 字體名稱(宋體)"UniGB-UCS2-H", // 字符編碼(簡體中文)BaseFont.NOT_EMBEDDED // 不嵌入字體(依賴系統字體));Font font = new Font(bf, size, type);font.setColor(BaseColor.BLACK);font.setSize(size);return font;}// 頁碼事件類public static class PageNumberEvent extends PdfPageEventHelper {@Overridepublic void onEndPage(PdfWriter writer, Document document) {// 跳過首頁(頁碼為1時不顯示)if (writer.getPageNumber() == 1 || writer.getPageNumber() == 2) {return;}PdfContentByte cb = writer.getDirectContent();try {// 設置字體,使用支持中文的字體(仍需設置,避免其他內容亂碼)Font font = getFont(10, Font.NORMAL);// 設置字體到內容字節cb.setFontAndSize(font.getBaseFont(), font.getSize());// 計算頁碼位置(底部居中)// 從第3頁開始,顯示實際頁碼-2(如第3頁顯示1,第4頁顯示2)String pageNumber = String.valueOf(writer.getPageNumber() - 2);float x = (document.left() + document.right()) / 2;float y = document.bottom() - 15; // 設置底部
// float y = document.top(); // 設置頂部// 添加頁碼cb.beginText();cb.setTextMatrix(x, y);cb.showTextAligned(Element.ALIGN_CENTER, pageNumber, x, y, 0);cb.endText();} catch (Exception e) {e.printStackTrace();}}}// 水印事件類public static class WatermarkEvent extends PdfPageEventHelper {@Overridepublic void onEndPage(PdfWriter writer, Document document) {// 跳過首頁(頁碼為1時不顯示水印)if (writer.getPageNumber() == 1) {return;}PdfContentByte canvas = writer.getDirectContentUnder();try {// 設置水印字體Font font = getFont(12, Font.BOLD);// 設置水印文本String watermarkText = "CSDN檢測報告";// 獲取頁面尺寸Rectangle pageSize = document.getPageSize();float width = pageSize.getWidth();float height = pageSize.getHeight();// 設置水印顏色和透明度canvas.saveState();// 創建 PdfGState 對象并設置透明度PdfGState gs = new PdfGState();gs.setFillOpacity(0.5f); // 填充透明度(0-1之間,0完全透明,1完全不透明)canvas.setGState(gs); // 將透明度應用到畫布canvas.setRGBColorFill(200, 200, 200); // 灰色canvas.setFontAndSize(font.getBaseFont(), font.getSize());canvas.setTextMatrix(0, 0);// 設置水印旋轉角度float angle = 30;// 計算水印位置并添加多個水印for (float y = -height / 2; y <= height * 1.5; y += 420) {for (float x = -width / 2; x <= width * 1.5; x += 300) {canvas.showTextAlignedKerned(Element.ALIGN_CENTER,watermarkText,x + width / 2,y + height / 2,angle);}}canvas.restoreState();} catch (Exception e) {e.printStackTrace();}}}}
實體類,水點字,就不寫在同一個類下了
package com.wy.tools;public class Staff {private String id;private String name;private String age;private String work;private String pic;// 無參構造方法public Staff() {}// 有參構造方法public Staff(String id, String name, String age, String work, String pic) {this.id = id;this.name = name;this.age = age;this.work = work;this.pic = pic;}// Getter 和 Setter 方法public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getWork() {return work;}public void setWork(String work) {this.work = work;}public String getPic() {return pic;}public void setPic(String pic) {this.pic = pic;}// 重寫 toString 方法@Overridepublic String toString() {return "Study{" +"id='" + id + '\'' +", name='" + name + '\'' +", age='" + age + '\'' +", work='" + work + '\'' +", pic='" + pic + '\'' +'}';}
}