通過java將數據導出為PDF,包扣合并單元格操作

最近項目中需要將查詢出來的表格數據以PDF形式導出,并且表格的形式包含橫向行與縱向列的單元格合并操作,導出的最終效果如圖所示:

首先引入操作依賴

<!--導出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>

最上面的基本信息是固定死的就是4*4的表格,這個創建起來 就比較簡單,主要是下面這個表格,需要從數據庫查出數據并循環進行展示,并且內容相同的列要進行合并。直接展示代碼:

主類對外調用方法:

@Operation(summary = "導出PDF")@PostMapping("/download")@SneakyThrows(Exception.class)public void download(Long id,HttpServletResponse response, HttpServletRequest request) {// 防止日志記錄獲取session異常request.getSession();// 設置編碼格式response.setContentType("application/pdf;charset=UTF-8");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("調試PDF", "UTF-8");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");contractDemandThreeRequestBaseService.download(id,response);}

具體實現類:

@Overridepublic void download(Long id, HttpServletResponse response) {//要下載的數據查詢數據部分我去掉了有需要自己根據業務取ContractDemandThreeRequestBaseDO baseDO = contractDemandThreeRequestBaseMapper.selectById(id);ContractDemandThreeRequestBaseRespVO base = ContractDemandThreeRequestBaseConvert.INSTANCE.convert(baseDO);base.setDeptName(ObjectUtil.isEmpty(deptService.getDept(base.getDeptId())) ? null : deptService.getDept(base.getDeptId()).getName());base.setProjectLeaderName(ObjectUtil.isEmpty(userService.getUser(base.getProjectLeaderId())) ? null : userService.getUser(base.getProjectLeaderId()).getNickname());//子表數據List<ContractDemandThreeQuestionDO> details = contractDemandThreeQuestionMapper.selectList(Wrappers.lambdaQuery(ContractDemandThreeQuestionDO.class).eq(ContractDemandThreeQuestionDO::getDemandId, id));//下面進行表格的創建、字體設置、合并單元格// 定義全局的字體靜態變量Font titlefont;Font headfont;Font keyfont = null;Font textfont = null;Font content = null;BaseFont bfChinese = null;// 最大寬度try {// 不同字體(這里定義為同一種字體:包含不同字號、不同style)這里我用的最后一個                content字體bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);titlefont = new Font(bfChinese, 16, Font.BOLD);headfont = new Font(bfChinese, 14, Font.BOLD);keyfont = new Font(bfChinese, 10, Font.BOLD);textfont = new Font(bfChinese, 15, Font.NORMAL);content = new Font(bfChinese, 10, Font.NORMAL);} catch (Exception e) {e.printStackTrace();}BaseFont bf;Font font = null;try {//創建字體bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);//使用字體并給出顏色font = new Font(bf, 20, Font.BOLD, BaseColor.BLACK);} catch (Exception e) {e.printStackTrace();}Document document = new Document();try {PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());//打開生成的pdf文件document.open();//設置內容Paragraph paragraph = new Paragraph("采購需求三問", font);paragraph.setAlignment(1);//引用字體document.add(paragraph);// 設置表格的列寬和列數float[] widths = {25f, 25f, 25f, 25f};PdfPTable table = new PdfPTable(widths);table.setSpacingBefore(20f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.setHeaderRows(1);table.getDefaultCell().setHorizontalAlignment(1);PdfPCell cell = null;//第一行:表格的名字cell = new PdfPCell(new Paragraph("采購項目名稱", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);//表格里面的值(下面都是同樣的操作)cell = new PdfPCell(new Paragraph(base.getProjectName(), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);//第二行cell = new PdfPCell(new Paragraph("項目編號", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);cell = new PdfPCell(new Paragraph(base.getProjectCode(), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell(new Paragraph("資金類型", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell(new Paragraph(getDitcValue("FundType", base.getFundType()), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);//第三行cell = new PdfPCell(new Paragraph("支出類別", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);cell = new PdfPCell(new Paragraph(getDitcValue("ExpenditureCategory", base.getExpenditureCategory()), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell(new Paragraph("預算含稅總金額(萬元)", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell(new Paragraph(String.valueOf(base.getBudgetIncludingTaxTotalMoney()), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);//第三行cell = new PdfPCell(new Paragraph("采購內容", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);cell = new PdfPCell(new Paragraph(base.getProcureContent(), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);//第四行cell = new PdfPCell(new Paragraph("需求部門", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);cell = new PdfPCell(new Paragraph(base.getDeptName(), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);cell = new PdfPCell(new Paragraph("項目負責人", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);table.addCell(cell);cell = new PdfPCell(new Paragraph(base.getProjectLeaderName(), content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell);// 設置表格的列寬和列數float[] widths2 = {25f, 25f, 25f};PdfPTable table2 = new PdfPTable(widths2);table2.setSpacingBefore(20f);// 設置表格寬度為100%table2.setWidthPercentage(100.0F);table2.setHeaderRows(1);table2.getDefaultCell().setHorizontalAlignment(1);//需求三問詳情信息標題欄cell = new PdfPCell(new Paragraph("需求三問", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(20);table2.addCell(cell);cell = new PdfPCell(new Paragraph("選擇項", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table2.addCell(cell);cell = new PdfPCell(new Paragraph("內容項", content));cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);table2.addCell(cell);//下面的代碼就是樣圖中底下的表格,需要動態構建數據//人員列表數據-第五行List<List<String>> doList = new ArrayList<>();if (CollectionUtils.isNotEmpty(details)) {//組裝數據for (ContractDemandThreeQuestionDO detail : details) {String natureType = "項目" + getDitcValue("QuestionType", detail.getDemandNatureType());String deviceGoodsType = getDitcValue(baseDO.getModelType(), detail.getItemValue());doList.add(Arrays.asList(natureType, deviceGoodsType, detail.getItemContent()));}makeData(doList, content, table2);//為兩個表格添加標題document.add(new Paragraph("\n"));document.add(new Paragraph("▋ 基本信息", content));document.add(new Paragraph("\n"));document.add(table);document.add(new Paragraph("\n"));document.add(new Paragraph("▋ 采購需求三問內容", content));document.add(new Paragraph("\n"));//將table2中數據相同的列合并單元格(橫向合并)document.add(table2);// 加水印(水印組成:下載人姓名-手機號-部門)PdfContentByte waterMar = pdfWriter.getDirectContentUnder();AtomicReference<String> text = new AtomicReference<>("");Long loginUserId = getLoginUserId();AdminUserRedisVO adminUserRedisVO = adminUserRedisDAO.get(loginUserId);Optional.ofNullable(adminUserRedisVO).ifPresent(vo -> {DeptDO dept = deptService.getDept(adminUserRedisVO.getDeptId());String deptName = Objects.nonNull(dept) ? dept.getName() : "";text.set(vo.getNickname() + "-" + vo.getMobile() + "-" + deptName);});addTextFullWaterMark(waterMar, text.get(), bfChinese);//關閉文檔document.close();} catch (DocumentException | IOException e) {e.printStackTrace();log.error("導出pdf失敗:{}", e);}}

補充說明:List<List<String>> doList = new ArrayList<>();

這個是我構建底下表格數據的格式,舉個例子,類似于:

List<List<String>> headList = new ArrayList<>();
headList.add(Arrays.asList(new String[]{"1", "2", "3"}));
headList.add(Arrays.asList(new String[]{"2", "6", "10"}));
headList.add(Arrays.asList(new String[]{"1", "12", "13"}));
headList.add(Arrays.asList(new String[]{"2", "9", "11"}));
headList.add(Arrays.asList(new String[]{"1", "8", "12"}));

根據自己的實際業務構建,我底下的表格是一個n*3的表格,只有三列,所以數據結構就相當于上面的兩層List結構,最外層的集合代表有多少行,嵌套的結合相當于多少列(這里就是三列),我將對象集合查詢出來后使用循環,將我所用到的數據組裝成這個示例的樣子。

重點來了,以下是合并單元格的方法:

makeData(doList, content, table2);
/*** 合并單元格方法** @param list        表頭數據  list中相連下標位置內容如果相同自動合并 上下位置內容相同自動合并* @param fontChinese 支持轉換中文的Font對象* @return*/private void makeData(List<List<String>> list, Font fontChinese, PdfPTable table2) {List<List<PdfPCell>> aa = new ArrayList<>();int length = list.get(0).size();//循環在外層,這里代表的是有幾列(其實是固定的,因為上面構建的時候,列數就是三列)//下面的循環不用管,是將你組裝的數據設置到單元格里for (int i = 0; i < list.size(); i++) {List<String> strings = list.get(i);int colNum = 1;List<PdfPCell> bb = new ArrayList<>();for (int j = 0; j < strings.size(); j++) {if (j + 1 < strings.size()) {if (strings.get(j).equals(strings.get(j + 1))) {colNum++;} else {PdfPCell cell = new PdfPCell();//合并列cell.setColspan(colNum);Paragraph elements = new Paragraph(strings.get(j), fontChinese);elements.setAlignment(1);cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);cell.setFixedHeight(30);cell.addElement(elements);bb.add(cell);for (int a = 1; a < colNum; a++) {bb.add(null);}colNum = 1;}} else {PdfPCell cell = new PdfPCell();cell.setColspan(colNum);Paragraph elements = new Paragraph(strings.get(j), fontChinese);cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setHorizontalAlignment(Element.ALIGN_CENTER);elements.setAlignment(1);cell.setFixedHeight(30);cell.addElement(elements);bb.add(cell);for (int a = 1; a < colNum; a++) {bb.add(null);}colNum = 1;}}aa.add(bb);}//合并算法for (int i = 0; i < length; i++) {int rowSpan = 1;for (int j = 0; j < aa.size(); j++) {if (aa.get(j).get(i) == null) {continue;}if (j + 1 < aa.size()) {if (aa.get(j + 1).get(i) != null&& aa.get(j).get(i).getCompositeElements().get(0).toString().equals(aa.get(j + 1).get(i).getCompositeElements().get(0).toString())) {rowSpan++;} else {aa.get(j - rowSpan + 1).get(i).setRowspan(rowSpan);for (int a = 1; a < rowSpan; a++) {aa.get(j - rowSpan + 1 + a).set(i, null);}rowSpan = 1;}} else {aa.get(j - rowSpan + 1).get(i).setRowspan(rowSpan);for (int a = 1; a < rowSpan; a++) {aa.get(j - rowSpan + 1 + a).set(i, null);}rowSpan = 1;}}break;}for (List<PdfPCell> a : aa) {for (PdfPCell pCell : a) {if (pCell != null) {table2.addCell(pCell);}}}}

這里將合并算法進行一個說明:外層循環其實還就是表示列,我這里是三列,他會依次循環三列,并將橫向與縱向的表格中有相同數據的都進行合并,也就是值相同的行進行合并,值相同的列也進行合并。但我的業務要求就是只合并第一列,所以我這里也沒有改算法,偷了個懶,直接在第一次循環后加了break,直接終止后面的循環。這個自己看自己的業務要求。代碼里的getDictValue方法是我自己獲取字典值的方法,可自行定義

加水印:

public static void addTextFullWaterMark(PdfContentByte waterMar, String text, BaseFont bf) {waterMar.beginText();PdfGState gs = new PdfGState();// 設置填充字體不透明度為0.2fgs.setFillOpacity(0.2f);waterMar.setFontAndSize(bf, 20);// 設置透明度waterMar.setGState(gs);// 設置水印對齊方式 水印內容 X坐標 Y坐標 旋轉角度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);}}// 設置水印顏色waterMar.setColorFill(BaseColor.GRAY);//結束設置waterMar.endText();waterMar.stroke();}

這個操作PDF的類很強大,基本上你想怎么導出,都可以進行調整

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

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

相關文章

【js獲取月份最后一天】

功能 獲取月份最后一天 代碼 function getLastDay(year, month) {//返回月份最后一天&#xff0c;不寫參數默認返回本月最后一天var date new Date(),date2, day;if (year undefined) year date.getFullYear(); //獲取今年年份if (month undefined) month date.getMont…

Linux- cron調度進程

cron 是一個 Unix 類操作系統中的時間調度守護進程&#xff0c;用于在特定的時間或間隔運行指定的命令或腳本。它非常適合自動化系統管理和維護任務&#xff0c;如備份、日志輪轉、系統監控等。以下是 cron 守護進程的詳細介紹。 cron 守護進程的工作原理 crontab 文件&#x…

上海市計算機學會競賽平臺2022年5月月賽丙組三數排序

題目描述 給定三個整數 &#x1d44e;,&#x1d44f;,&#x1d450;a,b,c&#xff0c;請將它們以從小到大的順序排序后輸出。 輸入格式 單獨一行&#xff1a;三個整數表示 &#x1d44e;,&#x1d44f;,&#x1d450;a,b,c。 輸出格式 單獨一行&#xff1a;表示按升序排列…

匯聚榮:拼多多長期沒有流量如何提高?

在電商的海洋中&#xff0c;拼多多以其獨特的團購模式吸引了眾多消費者的目光。然而&#xff0c;隨著市場競爭的加劇和消費者需求的多樣化&#xff0c;一些商家發現自家店鋪的流量持續低迷&#xff0c;銷售業績難以突破。面對這樣的挑戰&#xff0c;如何有效提升拼多多店鋪的客…

【Python】學生管理系統

為了了解Json以及在python中如何處理Json數據&#xff0c;我在這里整理了一段全面詳細的 Python 代碼&#xff0c;演示了如何加載、處理和操作 JSON 數據。該代碼包括讀取 JSON 數據、查詢學生信息、添加新學生、更新課程信息等操作。 示例代碼 import json# 示例 JSON 數據 …

深視 線掃相機 獲取點云數據

Qt hello - 專注于Qt的技術分享平臺 最近項目上用到了深視的線掃相機&#xff0c;集成了三天才搞定&#xff0c;分享下代碼。 順便吐槽一下&#xff0c;想用相機取圖&#xff0c;這么簡單的功能&#xff0c;搞得如此麻煩。 1&#xff0c;文檔有三份&#xff0c;就不能集成到…

【計算機畢業設計】springboot反詐科普平臺的設計與實現

相比于以前的傳統手工管理方式&#xff0c;智能化的管理方式可以大幅降低反詐科普平臺的運營人員成本&#xff0c;實現了反詐科普平臺的 標準化、制度化、程序化的管理&#xff0c;有效地防止了反詐科普平臺的隨意管理&#xff0c;提高了信息的處理速度和精確度&#xff0c;能夠…

python中字符串的 format() 方法

文章目錄 前言1、位置參數2、索引參數3、命名參數3、格式化參數 前言 format() 是 Python 字符串對象的方法&#xff0c;用于將值插入到格式化字符串的占位符中。它是一種靈活和強大的字符串格式化工具。format() 方法可以在字符串中使用占位符 {}&#xff0c;并通過傳遞參數將…

[vue] nvm

nvm ls // 看安裝的所有node.js的版本nvm list available // 查顯示可以安裝的所有node.js的版本可以在可選列表里。選擇任意版本安裝&#xff0c;比如安裝16.15.0 執行&#xff1a; nvm install 16.15.0安裝好了之后。可以執行&#xff1a; …

字符數組以及字符串相關的幾個函數

一.字符數組 1.定義&#xff1a;格式如下 char a[10]; //此處就表示定義了一個長度為10的字符數組 2.引用&#xff1a; 也和其余的數組一樣&#xff0c;是下標引用。 3.初始化&#xff1a; 如下代碼為字符數組初始化的幾種情況&#xff1a; int main() {char arr[5] {…

25考研英語長難句Day03

25考研英語長難句Day03 【a.詞組】【b.斷句】 多虧了電子學和微力學的不斷小型化&#xff0c;現在已經有一些機器人系統可以進行精確到毫米以下的腦部和骨骼手術&#xff0c;比技術高超的醫生用手能做到的精確得多。 【a.詞組】 詞組翻譯thanks to多虧了&#xff0c;由于cont…

【JavaEE進階】 Bean的作用域與生命周期

文章目錄 &#x1f343;Bean的作用域&#x1f6a9;作用域的使用&#x1f6a9;觀察Bean的作用域&#x1f388;單例作用域&#x1f388;多例作用域&#x1f388;請求作用域&#x1f388;會話作?域&#x1f388;Application作?域 &#x1f384;Bean的?命周期?總結 &#x1f34…

win11家庭中文版安裝docker,報錯 Docker Engine stopped

先引一下這位博主的鏈接超詳細Windows11家庭中文版系統安裝Docker-20230401_windows11安裝docker-CSDN博客&#xff0c;我到前五步(跳出頁面重啟)和博主都是一樣的&#xff0c;但是第六步我并沒有報錯&#xff0c;直接跳出docker界面 記錄一下我的解決辦法&#xff0c;首先按照…

金價又雙叒漲了!現貨黃金什么比較好

雖然近期有新聞顯示&#xff0c;國內的實物黃金價格出現大幅的下跌&#xff0c;但是從整體看&#xff0c;多個黃金投資品種的長期上升趨勢還是比較穩定的&#xff0c;因此我們會看到&#xff0c;很多投資者會趁現在這波下跌重新入場做多。那么投資黃金買什么比較好呢&#xff1…

Java中的類與對象-深入探索

在Java編程的世界里&#xff0c;類&#xff08;Class&#xff09;和對象&#xff08;Object&#xff09;是兩個核心概念。它們是面向對象編程&#xff08;OOP&#xff09;的基石&#xff0c;使得Java能夠處理復雜的數據結構和交互。本文將深入解析Java中的類和對象&#xff0c;…

淺述遙感技術在農業領域的應用

雖久未更新&#xff0c;但本文依舊延續以前敘述風格&#xff0c;即以通俗易懂方式描述關鍵問題。 本文章節安排如下&#xff1a; 簡述背景&#xff1b;介紹在農業領域的主要應用技術的關鍵問題&#xff1b;總結和實例介紹。 1 背景描述-何為遙感圖像&#xff1f; 一般來說&a…

如何向全國各大新聞網站投稿?

在信息爆炸的時代,新聞媒體的投稿工作對于單位的信息宣傳員來說,既是一項重要的職責,也是一項充滿挑戰的任務。作為一名信息宣傳員,我負責著單位的對外信息宣傳投稿工作,每個月都需要在各大媒體上發表文章,以展示單位的成果和風采。 然而,剛開始的投稿之路并不順暢。我習慣性地…

4種企業防泄密的辦法,強烈推薦第二種

4種企業防泄密的辦法&#xff0c;強烈推薦第二種 企業信息泄密常見的原因有內部人員、黑客、違規收集信息、第三方合作商&#xff0c;以下將為你詳細分析這些泄密原因以及應對的方法。 1、內部人員泄密 內部員工由于能夠接觸到敏感數據&#xff0c;成為主要的泄露數據群體。這…

springboot 序列化和反序列化

介紹 在Java中&#xff0c;序列化和反序列化是一種將對象轉換為字節流或將字節流轉換為對象的機制。通過序列化&#xff0c;可以將對象存儲到文件中、傳輸到網絡上&#xff0c;或者在分布式系統中進行對象的傳遞。本文將詳細介紹Java序列化和反序列化的原理、使用方法和常見應用…

優路教育:以實干、創新、永不言敗的精神內核,推動新時代職教發展

隨著“教育家精神”的提出&#xff0c;新時代下人民教師的職業內涵更為豐富&#xff0c;同時也被賦予了更為崇高的教育使命。強教必先強師&#xff0c;加強教師隊伍建設是建設教育強國重要的基礎工作。由此&#xff0c;打造一批勤勉敬業、創新實干的教師隊伍&#xff0c;成為了…