入口類:exportPdf
?
package xcsy.qms.webapi.service;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.StringUtils;
import com.ibm.icu.text.RuleBasedNumberFormat;
import com.lowagie.text.*;
import com.lowagie.text.Font;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.*;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.openapi.common.result.CustomApiResult;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import org.jsoup.Jsoup;
import xcsy.zjy.webapi.utils.AddImageToEachPageHeader;
import xcsy.zjy.webapi.utils.CheckBoxCellEvent;
import xcsy.zjy.webapi.utils.CheckBoxCellNotEvent;
import xcsy.zjy.webapi.utils.PdfPageUtil;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;/*** @program: xcsy-cosmic* @description: 準入報告pdf導出實現* @author: lyw* @create: 2025-02-13 09:31**/public class ReportsPDFService {private static final Log log = LogFactory.getLog(ReportsPDFService.class);// 定義全局的字體靜態變量Font headFont;Font headFont2;Font contentFont;Font titleFont;Font titleFont2;Font titleFontW;SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");public CustomApiResult<String> exportPdf(HttpServletRequest request, HttpServletResponse response) throws IOException {// 單據idString id = request.getParameter("id");DynamicObject accessReportsDO = BusinessDataServiceHelper.loadSingle(id, "eo45_access_reports");// 防止日志記錄獲取session異常request.getSession();// 設置編碼格式response.setContentType("application/pdf;charset=UTF-8");response.setCharacterEncoding("utf-8");SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");String fileName = URLEncoder.encode(accessReportsDO.getString("eo45_fac_name") + "聯盟工廠質量專項評審報告" + dateFormat.format(new Date()), "UTF-8");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".pdf");download(response, accessReportsDO);return CustomApiResult.success("成功");}private void download(HttpServletResponse response, DynamicObject reportData) {// 最大寬度try {// 不同字體(這里定義為同一種字體:包含不同字號、不同style)BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);headFont = new Font(bf, 18, Font.BOLD, new Color(0, 0, 0));headFont2 = new Font(bf, 14, Font.BOLD, new Color(0, 0, 0));titleFont = new Font(bf, 10, Font.BOLD, new Color(0, 103, 255));titleFont2 = new Font(bf, 12, Font.BOLD, new Color(0, 0, 0));titleFontW = new Font(bf, 12, Font.BOLD, new Color(251, 251, 251));contentFont = new Font(bfChinese, 10, Font.NORMAL, new Color(0, 0, 0));Document document = new Document(new RectangleReadOnly(842F, 595F));// 設置頁邊距document.setMargins(60, 60, 60, 30);PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());// 添加頁碼writer.setPageEvent(new PdfPageUtil());// 每頁表頭添加水印(圖片)writer.setPageEvent(new AddImageToEachPageHeader());// 打開生成的pdf文件document.open();// 標題1Paragraph paragraph = new Paragraph(reportData.getString("eo45_fac_name") + "聯盟工廠質量專項評審報告", headFont);paragraph.setAlignment(1);document.add(paragraph);// 評審總結DynamicObjectCollection resultsDO = reportData.getDynamicObjectCollection("eo45_report_results");DynamicObject entryResults = resultsDO.get(0);// -------基本信息buildBasicInformation(document, entryResults);// -------審核目的buildAuditAim(document, entryResults);// -------審核類型buildAuditType(document, reportData);// -------審核范圍buildAuditRange(document, entryResults);// -------審核內容buildAuditContent(document, entryResults);// -------審核方法buildAuditMethod(document, entryResults);// -------審核方背景資料buildAuditData(document, entryResults);// -------審核結論buildAuditResult(document, entryResults);// 標題2Paragraph paragraph2 = new Paragraph("質量專項評審“否決項”評分標準", headFont2);paragraph2.setAlignment(1);document.add(paragraph2);float[] widthsOne = {10f, 20f, 60f, 10f};PdfPTable tableOne = new PdfPTable(widthsOne);handleTableOne(reportData, tableOne);document.add(tableOne);// 標題3Paragraph paragraph3 = new Paragraph("分值匯總", headFont2);paragraph3.setAlignment(1);document.add(paragraph3);float[] widthsTwo = {60f, 20f, 20f};PdfPTable tableTwo = new PdfPTable(widthsTwo);handleTableTwo(reportData, tableTwo);document.add(tableTwo);// 標題4Paragraph paragraph4 = new Paragraph("聯盟工廠質量專項評審表", headFont2);paragraph4.setAlignment(1);document.add(paragraph4);float[] widthsThree = {5f, 15f, 30f, 25f, 5f, 5f, 5f, 7f, 25f};PdfPTable tableThree = new PdfPTable(widthsThree);handleTableThree(reportData, tableThree);document.add(tableThree);// 關閉文檔document.close();} catch (DocumentException e) {log.error("導出pdf失敗DocumentException:{}", e);} catch (Exception e) {log.error("導出pdf失敗Exception:{}", e);}}private void handleTableThree(DynamicObject reportData, PdfPTable table) {// 項目分類集合DynamicObjectCollection projectCollection = reportData.getDynamicObjectCollection("eo45_report_projects");// 按eo45_project_id分組Map<String, String> projectMap = new LinkedHashMap<>();// 獲取項目,以項目id作為key,排除第一項否決項,按項目seq排序projectCollection.stream().filter(e -> !"0".equals(e.getString("eo45_project_seq"))).sorted(Comparator.comparing(dynamicObject -> dynamicObject.getString("eo45_project_seq"))).forEach(e -> projectMap.put(e.getString("eo45_project_id"), e.getString("eo45_project")));// 段落在其上方留出的空間量table.setSpacingBefore(10f);// 設置表格寬度為100%// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.setHeaderRows(2);table.getDefaultCell().setHorizontalAlignment(1);// 第一行PdfPCell titleCell1 = createCenteredCellForTable("條款編號", 30, new Color(40, 120, 255), titleFontW, "border", "center");titleCell1.setRowspan(2);table.addCell(titleCell1);PdfPCell titleCell2 = createCenteredCellForTable("條款性質", 0, new Color(40, 120, 255), titleFontW, "border", "center");titleCell2.setRowspan(2);table.addCell(titleCell2);PdfPCell titleCell3 = createCenteredCellForTable("審核條款標準", 0, new Color(40, 120, 255), titleFontW, "border", "center");titleCell3.setRowspan(2);table.addCell(titleCell3);PdfPCell titleCell4= createCenteredCellForTable("審核正面發現", 0, new Color(40, 120, 255), titleFontW, "border", "center");titleCell4.setRowspan(2);table.addCell(titleCell4);PdfPCell titleCell10 = createCenteredCellForTable("審核記錄及說明", 0, new Color(40, 120, 255), titleFontW, "border", "center");titleCell10.setColspan(6);table.addCell(titleCell10);PdfPCell titleCell5 = createCenteredCellForTable("審核配分", 0, new Color(40, 120, 255), titleFontW, "border", "center");table.addCell(titleCell5);PdfPCell titleCell6 = createCenteredCellForTable("符合程度", 0, new Color(40, 120, 255), titleFontW, "border", "center");table.addCell(titleCell6);PdfPCell titleCell7 = createCenteredCellForTable("實際得分", 0, new Color(40, 120, 255), titleFontW, "border", "center");table.addCell(titleCell7);PdfPCell titleCell8 = createCenteredCellForTable("不符合類型", 0, new Color(40, 120, 255), titleFontW, "border", "center");table.addCell(titleCell8);PdfPCell titleCell9 = createCenteredCellForTable("問題描述", 0, new Color(40, 120, 255), titleFontW, "border", "center");table.addCell(titleCell9);int[] x = {0}; // 項目序號// 添加一級項目projectMap.forEach((projectId, projectName) -> {x[0]++;// 1轉一RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.CHINA, RuleBasedNumberFormat.SPELLOUT);String result = formatter.format(x[0]);// 項目總分BigDecimal projectScoreAll = projectCollection.stream().filter(e -> projectId.equals(e.getString("eo45_project_id"))).map(e -> e.getBigDecimal("eo45_project_scoreall")).findFirst().orElse(BigDecimal.ZERO);PdfPCell projectCell = new PdfPCell(new Paragraph(result + "、" + projectName+ "(" + projectScoreAll.setScale(2, RoundingMode.HALF_UP) + "分)", titleFont));projectCell.setColspan(9);tableCellStyle(projectCell, new Color(237, 239, 240), new Color(242, 247, 255), "border");table.addCell(projectCell);int y = 0; // 分類序號// 二級分類for (DynamicObject categorysDynamicObject : projectCollection) {if (projectId.equals(categorysDynamicObject.getString("eo45_project_id"))) {y++;PdfPCell cellClass = new PdfPCell(new Paragraph(x[0] + "." + y + " "+ categorysDynamicObject.getString("eo45_sort_name") + "(" + categorysDynamicObject.getBigDecimal("eo45_sort_scoreall").setScale(2, RoundingMode.HALF_UP) + "分)", titleFont));cellClass.setColspan(9);tableCellStyle(cellClass, new Color(237, 239, 240), new Color(242, 247, 255), "border");table.addCell(cellClass);// 三級分類內容DynamicObjectCollection contents = categorysDynamicObject.getDynamicObjectCollection("eo45_report_contents");int z = 0; // 內容序號for (DynamicObject content : contents) {z++;String ratio = ""; // 系數// 單選題JSONArray optionArray = JSONArray.parseArray(content.getString("eo45_option_json_tag"));// 問題JSONArray problemArray = JSONArray.parseArray(content.getString("eo45_problem_json_tag"));// 合并行的數量int rowspan = 1;if (problemArray != null && !problemArray.isEmpty()) {rowspan = problemArray.size();}if (optionArray != null && !optionArray.isEmpty()) {for (int j = 0; j < optionArray.size(); j++) {JSONObject option = optionArray.getJSONObject(j);if (option.getInteger("choose") == 1) {if (!"N/A".equals(option.getString("scoreCoefficient"))&& StringUtils.isNotEmpty(option.getString("scoreCoefficient"))) {double decimal = Double.parseDouble(option.getString("scoreCoefficient"));// 轉換為百分比并四舍五入到最接近的整數int percentage = (int) Math.round(decimal * 100);ratio = percentage + "%";}break;}}}PdfPCell cell1 = new PdfPCell(new Paragraph(x[0] + "." + y + "." + z, contentFont));cell1.setRowspan(rowspan);PdfPCell cell2 = new PdfPCell(new Paragraph(content.getString("eo45_import_area"), contentFont));cell2.setRowspan(rowspan);String contentContext = "";if (StringUtils.isNotBlank(content.getString("eo45_project_context"))) {org.jsoup.nodes.Document documentResult = Jsoup.parse(content.getString("eo45_project_context"));contentContext = documentResult.text();}PdfPCell cell3 = new PdfPCell(new Paragraph(contentContext, contentFont));cell3.setRowspan(rowspan);PdfPCell cell4 = new PdfPCell(new Paragraph(content.getString("eo45_content_suggestion"), contentFont));cell4.setRowspan(rowspan);PdfPCell cell5 = new PdfPCell(new Paragraph(String.valueOf(content.getBigDecimal("eo45_sum_score").setScale(2, RoundingMode.HALF_UP)), contentFont));cell5.setRowspan(rowspan);PdfPCell cell6 = new PdfPCell(new Paragraph(ratio, contentFont));cell6.setRowspan(rowspan);PdfPCell cell7 = new PdfPCell(new Paragraph(String.valueOf(content.getBigDecimal("eo45_content_score").setScale(2, RoundingMode.HALF_UP)), contentFont));cell7.setRowspan(rowspan);//單元格對齊方式水平、垂直tableCellStyle(cell1, new Color(237, 239, 240), null, "border");cell1.setFixedHeight(20);tableCellStyle(cell2, new Color(237, 239, 240), null, "border");tableCellStyle(cell3, new Color(237, 239, 240), null, "border");tableCellStyle(cell4, new Color(237, 239, 240), null, "border");tableCellStyle(cell5, new Color(237, 239, 240), null, "border");tableCellStyle(cell6, new Color(237, 239, 240), null, "border");tableCellStyle(cell7, new Color(237, 239, 240), null, "border");table.addCell(cell1);table.addCell(cell2);table.addCell(cell3);table.addCell(cell4);table.addCell(cell5);table.addCell(cell6);table.addCell(cell7);PdfPCell cell8, cell9;if (problemArray != null && !problemArray.isEmpty()) {for (int j = 0; j < problemArray.size(); j++) {JSONObject problem = problemArray.getJSONObject(j);String selectContent = "";JSONArray options = JSONArray.parseArray(problem.getString("options"));for (int k = 0; k < options.size(); k++) {JSONObject option = options.getJSONObject(k);if (option.getInteger("choose") == 1) {selectContent = option.getString("selectContent");break;}}cell8 = new PdfPCell(new Paragraph(selectContent, contentFont));cell9 = new PdfPCell(new Paragraph(problem.getString("problem_desc"), contentFont));tableCellStyle(cell8, new Color(237, 239, 240), null, "border");tableCellStyle(cell9, new Color(237, 239, 240), null, "border");table.addCell(cell8);table.addCell(cell9);}} else {cell8 = new PdfPCell(new Paragraph("", contentFont));cell9 = new PdfPCell(new Paragraph("", contentFont));tableCellStyle(cell8, new Color(237, 239, 240), null, "border");tableCellStyle(cell9, new Color(237, 239, 240), null, "border");table.addCell(cell8);table.addCell(cell9);}}}}});}private void handleTableTwo(DynamicObject reportData, PdfPTable table) {// 項目分類集合DynamicObjectCollection projectCollection = reportData.getDynamicObjectCollection("eo45_report_projects");// 按eo45_project_id分組// Map<String, List<DynamicObject>> projectMap = projectCollection.stream().collect(Collectors.groupingBy(dynamicObject -> dynamicObject.getString("eo45_project_id")));Map<String, String> projectMap = new LinkedHashMap<>();// 獲取項目,以項目id作為key,排除第一項否決項,按項目seq排序projectCollection.stream().filter(e -> !"0".equals(e.getString("eo45_project_seq"))).sorted(Comparator.comparing(dynamicObject -> dynamicObject.getString("eo45_project_seq"))).forEach(e -> projectMap.put(e.getString("eo45_project_id"), e.getString("eo45_project")));// 段落在其上方留出的空間量table.setSpacingBefore(10f);// 距離下方空間table.setSpacingAfter(20f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.setHeaderRows(1);table.getDefaultCell().setHorizontalAlignment(1);// 第一行table.addCell(createCenteredCellForTable("主要項目", 30, new Color(242, 247, 255), titleFont2, null, null));table.addCell(createCenteredCellForTable("項目配分", 0, new Color(242, 247, 255), titleFont2, null, null));table.addCell(createCenteredCellForTable("實際得分", 0, new Color(242, 247, 255), titleFont2, null, null));// 計數int[] projectSeq = {0};// 添加一級項目final BigDecimal[] allScore = {BigDecimal.ZERO}; // 審核分數合計final BigDecimal[] allGetScore = {BigDecimal.ZERO}; // 審核得分projectMap.forEach((projectId, projectName) -> {projectSeq[0]++;// 1轉一RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.CHINA, RuleBasedNumberFormat.SPELLOUT);String result = formatter.format(projectSeq[0]);PdfPCell cell1 = new PdfPCell(new Paragraph(result + "、" + projectName, contentFont));// 獲取對應項目的總分和得分BigDecimal eo45ProjectScoreAll = projectCollection.stream().filter(e -> projectId.equals(e.getString("eo45_project_id"))).map(e -> e.getBigDecimal("eo45_project_scoreall")).findFirst().orElse(BigDecimal.ZERO);allScore[0] = allScore[0].add(eo45ProjectScoreAll);BigDecimal eo45ProjectScore = projectCollection.stream().filter(e -> projectId.equals(e.getString("eo45_project_id"))).map(e -> e.getBigDecimal("eo45_project_score")).findFirst().orElse(BigDecimal.ZERO);allGetScore[0] = allGetScore[0].add(eo45ProjectScore);PdfPCell cell2 = new PdfPCell(new Paragraph(String.valueOf(eo45ProjectScoreAll.setScale(2, RoundingMode.HALF_UP)), contentFont));PdfPCell cell3 = new PdfPCell(new Paragraph(String.valueOf(eo45ProjectScore.setScale(2, RoundingMode.HALF_UP)), contentFont));//單元格對齊方式水平、垂直tableCellStyle(cell1, new Color(237, 239, 240), new Color(242, 247, 255), null);cell1.setFixedHeight(20);tableCellStyle(cell2, new Color(237, 239, 240), new Color(242, 247, 255), null);tableCellStyle(cell3, new Color(237, 239, 240), new Color(242, 247, 255), null);table.addCell(cell1);table.addCell(cell2);table.addCell(cell3);// 項目下添加二級分類int sortSeq = 0;for (DynamicObject categorysDynamicObject : projectCollection) {if (projectId.equals(categorysDynamicObject.getString("eo45_project_id"))) {sortSeq++;PdfPCell cell4 = new PdfPCell(new Paragraph(projectSeq[0] + "." + sortSeq + " " + categorysDynamicObject.getString("eo45_sort_name"), contentFont));PdfPCell cell5 = new PdfPCell(new Paragraph(String.valueOf(categorysDynamicObject.getBigDecimal("eo45_sort_scoreall").setScale(2, RoundingMode.HALF_UP)), contentFont));PdfPCell cell6 = new PdfPCell(new Paragraph(String.valueOf(categorysDynamicObject.getBigDecimal("eo45_sort_score").setScale(2, RoundingMode.HALF_UP)), contentFont));tableCellStyle(cell4, new Color(237, 239, 240), null, null);cell4.setFixedHeight(20);tableCellStyle(cell5, new Color(237, 239, 240), null, null);tableCellStyle(cell6, new Color(237, 239, 240), null, null);table.addCell(cell4);table.addCell(cell5);table.addCell(cell6);}}});// 添加分匯總PdfPCell cell4 = new PdfPCell(new Paragraph("審核分合計", titleFont));PdfPCell cell5 = new PdfPCell(new Paragraph(String.valueOf(allScore[0].setScale(2, RoundingMode.HALF_UP)), titleFont));PdfPCell cell6 = new PdfPCell(new Paragraph(String.valueOf(allGetScore[0].setScale(2, RoundingMode.HALF_UP)), titleFont));tableCellStyle(cell4, new Color(237, 239, 240), new Color(242, 247, 255), null);cell4.setFixedHeight(20);tableCellStyle(cell5, new Color(237, 239, 240), new Color(242, 247, 255), null);tableCellStyle(cell6, new Color(237, 239, 240), new Color(242, 247, 255), null);table.addCell(cell4);table.addCell(cell5);table.addCell(cell6);}private void handleTableOne(DynamicObject accessReportDO, PdfPTable table) {// 項目分類集合DynamicObjectCollection projectCollection = accessReportDO.getDynamicObjectCollection("eo45_report_projects");// 獲取“否決項”數據DynamicObject oneProject = projectCollection.stream().filter(e -> "0".equals(e.getString("eo45_project_seq"))).collect(Collectors.toList()).get(0);DynamicObjectCollection contents = oneProject.getDynamicObjectCollection("eo45_report_contents");// 段落在其上方留出的空間量table.setSpacingBefore(10f);// 距離下方空間table.setSpacingAfter(20f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.setHeaderRows(1);table.getDefaultCell().setHorizontalAlignment(1);// 第一行table.addCell(createCenteredCellForTable("序號", 30, new Color(242, 247, 255), titleFont2, null, null));table.addCell(createCenteredCellForTable("關鍵領域", 0, new Color(242, 247, 255), titleFont2, null, null));table.addCell(createCenteredCellForTable("檢查項目內容", 0, new Color(242, 247, 255), titleFont2, null, null));table.addCell(createCenteredCellForTable("是/否", 0, new Color(242, 247, 255), titleFont2, null, null));// 數據for (int i = 0; i < contents.size(); i++) {PdfPCell cell1 = new PdfPCell(new Paragraph(String.valueOf(i + 1), contentFont));PdfPCell cell2 = new PdfPCell(new Paragraph(contents.get(i).getString("eo45_import_area"), contentFont));// 富文本處理String contentContext = "";if (StringUtils.isNotBlank(contents.get(i).getString("eo45_project_context"))) {org.jsoup.nodes.Document documentResult = Jsoup.parse(contents.get(i).getString("eo45_project_context"));contentContext = documentResult.text();}PdfPCell cell3 = new PdfPCell(new Paragraph(contentContext, contentFont));// 獲取選項JSONArray optionJsonArray = JSONArray.parseArray(contents.get(i).getString("eo45_option_json_tag"));String selectContent = "";if (optionJsonArray != null && !optionJsonArray.isEmpty()) {for (int j = 0; j < optionJsonArray.size(); j++) {JSONObject optionJson = optionJsonArray.getJSONObject(j);if (optionJson.getInteger("choose") == 1) {selectContent = optionJson.getString("options_content");break;}}}PdfPCell cell4 = new PdfPCell(new Paragraph(selectContent, contentFont));tableCellStyle(cell1, new Color(237, 239, 240), null, null);//單元格對齊方式水平、垂直cell1.setFixedHeight(20);tableCellStyle(cell2, new Color(237, 239, 240), null, null);tableCellStyle(cell3, new Color(237, 239, 240), null, null);tableCellStyle(cell4, new Color(237, 239, 240), null, null);table.addCell(cell1);table.addCell(cell2);table.addCell(cell3);table.addCell(cell4);}}private void buildAuditResult(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核結論"));// 設置兩列float[] widths = {20f, 80f};PdfPTable table = new PdfPTable(widths);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell("業務審核意見:", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_conclusion_bus"), 0, null, contentFont));table.addCell(createCenteredCell("研發審核意見:", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_conclusion_dev"), 0, null, contentFont));table.addCell(createCenteredCell("質量審核意見:", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_obj"), 0, null, contentFont));table.addCell(createCenteredCell("數字化能力評估審核意見:", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_conclusion_qu"), 0, null, contentFont));table.addCell(createCenteredCell("綜合意見:", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_conclusion_res"), 0, null, contentFont));document.add(table);}private void buildAuditData(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核方背景資料"));PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(entryResults.getString("eo45_audit_information"), 0, null, contentFont));document.add(table);}private void buildAuditMethod(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核方法"));PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(entryResults.getString("eo45_audit_method"), 0, null, contentFont));document.add(table);}private void buildAuditContent(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核內容"));PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(entryResults.getString("eo45_audit_context"), 0, null, contentFont));document.add(table);}private void buildAuditRange(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核范圍"));PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(entryResults.getString("eo45_audit_scope"), 0, null, contentFont));document.add(table);}private void buildAuditType(Document document, DynamicObject data) throws DocumentException {// 設置標題document.add(createTitle("審核類型"));float[] widths = {2f, 8f, 2f, 8f, 2f, 8f, 70f};PdfPTable table = new PdfPTable(widths);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);PdfPCell cell = createCenteredCell("準入審核", 0, null, contentFont);PdfPCell cell2 = new PdfPCell();// 添加復選框繪制事件到單元格if ("0".equals(data.getString("eo45_report_type"))) {cell2.setCellEvent(new CheckBoxCellEvent());} else {cell2.setCellEvent(new CheckBoxCellNotEvent());}cell2.setHorizontalAlignment(PdfPCell.ALIGN_RIGHT);cell2.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);cell2.setBorder(Rectangle.NO_BORDER);table.addCell(cell2);table.addCell(cell);PdfPCell cell3 = createCenteredCell("年度審核", 0, null, contentFont);PdfPCell cell4 = new PdfPCell();// 添加復選框繪制事件到單元格if ("1".equals(data.getString("eo45_report_type"))) {cell4.setCellEvent(new CheckBoxCellEvent());} else {cell4.setCellEvent(new CheckBoxCellNotEvent());}cell4.setHorizontalAlignment(PdfPCell.ALIGN_RIGHT);cell4.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);cell4.setBorder(Rectangle.NO_BORDER);table.addCell(cell4);table.addCell(cell3);PdfPCell cell5 = createCenteredCell("跟蹤審核", 0, null, contentFont);PdfPCell cell6 = new PdfPCell();// 添加復選框繪制事件到單元格if ("2".equals(data.getString("eo45_report_type"))) {cell6.setCellEvent(new CheckBoxCellEvent());} else {cell6.setCellEvent(new CheckBoxCellNotEvent());}cell6.setHorizontalAlignment(PdfPCell.ALIGN_RIGHT);cell6.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);cell6.setBorder(Rectangle.NO_BORDER);table.addCell(cell6);table.addCell(cell5);PdfPCell cell7 = new PdfPCell();cell7.setBorder(Rectangle.NO_BORDER);table.addCell(cell7);document.add(table);}private void buildAuditAim(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("審核目的"));PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(entryResults.getString("eo45_audit_purpose"), 0, null, contentFont));document.add(table);}private void buildBasicInformation(Document document, DynamicObject entryResults) throws DocumentException {// 設置標題document.add(createTitle("基本信息"));// 設置兩列float[] widths = {17f, 83f};PdfPTable table = new PdfPTable(widths);// 段落在其上方留出的空間量table.setSpacingBefore(5f);// 設置表格寬度為100%table.setWidthPercentage(100.0F);table.addCell(createCenteredCell("審核對象", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_obj"), 0, null, contentFont));table.addCell(createCenteredCell("審核地址", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_addr"), 0, null, contentFont));table.addCell(createCenteredCell("審核日期", 15, null, contentFont));table.addCell(createCenteredCell(format.format(entryResults.getDate("eo45_audit_date")), 0, null, contentFont));table.addCell(createCenteredCell("審核人員", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_audit_person"), 0, null, contentFont));table.addCell(createCenteredCell("被審核方人員", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_beaudited_person"), 0, null, contentFont));table.addCell(createCenteredCell("本次審核后評級", 15, null, contentFont));table.addCell(createCenteredCell(entryResults.getString("eo45_score") + "級", 0, null, contentFont));document.add(table);}/*** 標題樣式** @param value 標題* @return*/public PdfPTable createTitle(String value) {PdfPTable table = new PdfPTable(1);// 段落在其上方留出的空間量table.setSpacingBefore(5f);if ("基本信息".equals(value)) {table.setSpacingBefore(15f);}table.setWidthPercentage(100.0F);table.addCell(createCenteredCell(value, 20, new Color(242, 247, 255), titleFont));return table;}/*** 創建cell基本內容** @param value 值* @param fixedHeight 高度* @param color 背景色* @param font 字體樣式* @return*/public PdfPCell createCenteredCell(String value, float fixedHeight, Color color, Font font) {Paragraph paragraph = new Paragraph(value, font);// paragraph.setLeading(10);PdfPCell cell = new PdfPCell(paragraph);// 水平對齊cell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT);// 垂直對齊// cell.setVerticalAlignment(Element.ALIGN_MIDDLE);// 邊框cell.setBorder(Rectangle.NO_BORDER);// 設置背景色if (color != null) {cell.setBackgroundColor(color);}// 設置單元格高度if (fixedHeight != 0) {cell.setFixedHeight(fixedHeight);}return cell;}/*** 表格表頭** @param value* @param fixedHeight* @param color* @param font* @return*/private PdfPCell createCenteredCellForTable(String value, float fixedHeight, Color color, Font font, String border, String alignment) {PdfPCell cell = new PdfPCell(new Paragraph(value, font));cell.setHorizontalAlignment(Element.ALIGN_LEFT);if ("center".equals(alignment)) {cell.setHorizontalAlignment(Element.ALIGN_CENTER);}cell.setVerticalAlignment(Element.ALIGN_MIDDLE);cell.setBorderColor(new Color(230, 230, 230));if (border == null) {cell.setBorder(Rectangle.TOP | Rectangle.BOTTOM);}// 設置背景色if (color != null) {cell.setBackgroundColor(color);}// 設置單元格高度if (fixedHeight != 0) {cell.setFixedHeight(fixedHeight);}return cell;}/*** 設置單元格樣式** @param cell*/private void tableCellStyle(PdfPCell cell, Color borderColor, Color backgroundColor, String border) {cell.setHorizontalAlignment(Element.ALIGN_LEFT);cell.setVerticalAlignment(Element.ALIGN_MIDDLE);// 只顯示上下邊框if (border == null) {cell.setBorder(Rectangle.TOP | Rectangle.BOTTOM);}// 邊框顏色if (borderColor != null) {cell.setBorderColorTop(borderColor);cell.setBorderColorBottom(borderColor);}// 背景色if (backgroundColor != null) {cell.setBackgroundColor(backgroundColor);}cell.setBorderColor(new Color(230, 230, 230));}}?
?頭部添加水印/圖片工具類
package xcsy.zjy.webapi.utils;import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfWriter;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;import java.io.IOException;
import java.net.URL;/*** @program: xcsy-cosmic* @description: pdf添加頭部水印圖片* @author: lyw* @create: 2025-02-17 16:51**/public class AddImageToEachPageHeader extends PdfPageEventHelper {private static final Log log = LogFactory.getLog(AddImageToEachPageHeader.class);// 圖片路徑public static final String IMG = "img/logo.png";// 圖片路徑public static final String LINEIMG = "img/line.png";// 自定義頁面事件監聽器Image headerImage;Image lineImage;public AddImageToEachPageHeader() throws IOException, DocumentException {URL resourceUrl = AddImageToEachPageHeader.class.getClassLoader().getResource(IMG);assert resourceUrl != null;headerImage = Image.getInstance(resourceUrl);// scaleToFit按比例縮放圖像、調整大小以適應您的需求headerImage.scaleToFit(50, 50);URL resourceUrl2 = AddImageToEachPageHeader.class.getClassLoader().getResource(LINEIMG);assert resourceUrl2 != null;lineImage = Image.getInstance(resourceUrl2);}@Overridepublic void onEndPage(PdfWriter writer, Document document) {try {// 計算圖片1放置的位置// float x = (document.right() - document.left()) / 2 + document.leftMargin() - headerImage.getScaledWidth() / 2; // 居中float x = document.leftMargin();// 適當調整距離頂部的距離float y = document.top() + headerImage.getScaledHeight() + 10;headerImage.setAbsolutePosition(x, y);writer.getDirectContent().addImage(headerImage);// 圖2// 獲取頁面寬度float pageWidth = document.getPageSize().getWidth();float x2 = 0;// 適當調整距離頂部的距離float y2 = document.top() + headerImage.getScaledHeight();// scaleAbsolute直接設置圖像的確切寬度和高度,不考慮原始寬高比。lineImage.scaleAbsolute(pageWidth, 10);lineImage.setAbsolutePosition(x2, y2);writer.getDirectContent().addImage(lineImage);} catch (DocumentException e) {log.error("pdf添加頭部水印圖片DocumentException:{}", e);}}}
復選框(選中)繪制工具類
package xcsy.zjy.webapi.utils;import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPCellEvent;
import com.lowagie.text.pdf.PdfPTable;import java.awt.*;/*** @program: xcsy-cosmic* @description: pdf復選框* @author: lyw* @create: 2025-02-18 10:30**/public class CheckBoxCellEvent implements PdfPCellEvent {@Overridepublic void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];// 繪制復選框的位置和大小float x = position.getLeft() + 2; // X坐標,相對于單元格左下角float y = position.getBottom() + 2; // Y坐標,相對于單元格左下角float size = 10; // 復選框大小// 開始寫入操作canvas.saveState();// 繪制復選框canvas.rectangle(x, y, size, size);canvas.setColorStroke(new Color( 190, 198, 205));canvas.stroke();// 確保勾選標記的起點、中點和終點位置合理,以形成一個清晰的√符號float rotatedMarkStartX = x + size - 2; // 原始markStartX關于x軸+size的對稱點float rotatedMarkStartY = y + size - ((size / 2) - 2); // 原始markStartY關于y軸+size的對稱點float rotatedMarkMidX = x + size - (size / 2); // 原始markMidX關于x軸+size的對稱點float rotatedMarkMidY = y + 3; // 原始markMidY關于y軸+size的對稱點float rotatedMarkEndX = x + 2; // 原始markEndX關于x軸+size的對稱點float rotatedMarkEndY = y + size - 4; // 原始markEndY關于y軸+size的對稱點canvas.moveTo(rotatedMarkStartX, rotatedMarkStartY); // 設置旋轉后的起點canvas.lineTo(rotatedMarkMidX, rotatedMarkMidY); // 經過旋轉后的中間點canvas.lineTo(rotatedMarkEndX, rotatedMarkEndY); // 結束于旋轉后的右上角canvas.setColorStroke(new Color(0, 103, 255));canvas.setColorFill(new Color(0, 103, 255));canvas.stroke();// 結束寫入操作canvas.restoreState();}
}
復選框(不選中)繪制工具類
package xcsy.zjy.webapi.utils;import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPCellEvent;
import com.lowagie.text.pdf.PdfPTable;import java.awt.*;/*** @program: xcsy-cosmic* @description: pdf復選框(不選中)* @author: lyw* @create: 2025-02-18 10:30**/public class CheckBoxCellNotEvent implements PdfPCellEvent {@Overridepublic void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {PdfContentByte canvas = canvases[PdfPTable.LINECANVAS];// 繪制復選框的位置和大小float x = position.getLeft() + 2; // X坐標,相對于單元格左下角float y = position.getBottom() + 2; // Y坐標,相對于單元格左下角float size = 10; // 復選框大小// 開始寫入操作canvas.saveState();// 繪制復選框canvas.rectangle(x, y, size, size);canvas.setColorStroke(new Color( 190, 198, 205));canvas.stroke();// 結束寫入操作canvas.restoreState();}
}
頁碼工具類
package xcsy.zjy.webapi.utils;import com.lowagie.text.*;
import com.lowagie.text.pdf.*;import java.io.IOException;/*** @Author xx* @Date 2023/12/15 10:05* @Description: 導出pdf添加頁數* @Version 1.0*/
public class PdfPageUtil extends PdfPageEventHelper {/*** 頁眉*///public String header = "itext測試頁眉";/*** 文檔字體大小,頁腳頁眉最好和文本大小一致*/public int presentFontSize = 9;/*** 文檔頁面大小,最好前面傳入,否則默認為A4紙張*/public Rectangle pageSize = PageSize.A4;// 模板public PdfTemplate total;// 基礎字體對象public BaseFont bf = null;// 利用基礎字體生成的字體對象,一般用于生成中文文字public Font fontDetail = null;/**** 無參構造方法.**/public PdfPageUtil() {}/**** 構造方法.** @param** @param presentFontSize* 數據體字體大小* @param pageSize* 頁面文檔大小,A4,A5,A6橫轉翻轉等Rectangle對象*/public PdfPageUtil( int presentFontSize, Rectangle pageSize) {this.presentFontSize = presentFontSize;this.pageSize = pageSize;}public void setPresentFontSize(int presentFontSize) {this.presentFontSize = presentFontSize;}/**** 文檔打開時創建模板*/@Overridepublic void onOpenDocument(PdfWriter writer, Document document) {// 共 頁 的矩形的長寬高total = writer.getDirectContent().createTemplate(50, 50);}/****關閉每頁的時候,寫入頁眉,寫入'第幾頁共'這幾個字。*/@Overridepublic void onEndPage(PdfWriter writer, Document document) {this.addPage(writer, document);}//加分頁public void addPage(PdfWriter writer, Document document){//設置分頁頁眉頁腳字體try {if (bf == null) {bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);}if (fontDetail == null) {fontDetail = new Font(bf, presentFontSize, Font.NORMAL);// 數據體字體}} catch (DocumentException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}// 1.寫入頁眉
// ColumnText.showTextAligned(writer.getDirectContent(),
// Element.ALIGN_LEFT, new Phrase(header, fontDetail),
// document.left(), document.top() + 20, 0);// 2.寫入前半部分的 第 X頁/共int pageS = writer.getPageNumber();//String foot1 = "第 " + pageS + " 頁 /共";String foot1 = pageS +"/";Phrase footer = new Phrase(foot1, fontDetail);// 3.計算前半部分的foot1的長度,后面好定位最后一部分的'Y頁'這倆字的x軸坐標,字體長度也要計算進去 = lenfloat len = bf.getWidthPoint(foot1, presentFontSize);// 4.拿到當前的PdfContentBytePdfContentByte cb = writer.getDirectContent();// 5.寫入頁腳1,x軸就是(右margin+左margin + right() -left()- len)/2.0FColumnText.showTextAligned(cb,Element.ALIGN_CENTER,footer,(document.rightMargin() + document.right()+ document.leftMargin() - document.left() - len) / 2.0F ,document.bottom() - 10, 0);cb.addTemplate(total, (document.rightMargin() + document.right()+ document.leftMargin() - document.left()) / 2.0F ,document.bottom() - 10); // 調節模版顯示的位置}// //加水印
// public void addWatermark(PdfWriter writer){
// // 水印圖片
// Image image;
// try {
// image = Image.getInstance("./web/images/001.jpg");
// PdfContentByte content = writer.getDirectContentUnder();
// content.beginText();
// // 開始寫入水印
// for(int k=0;k<5;k++){
// for (int j = 0; j <4; j++) {
// image.setAbsolutePosition(150*j,170*k);
// content.addImage(image);
// }
// }
// content.endText();
// } catch (IOException | DocumentException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }/**** 關閉文檔時,替換模板,完成整個頁眉頁腳組件*/@Overridepublic void onCloseDocument(PdfWriter writer, Document document) {// 關閉文檔的時候,將模板替換成實際的 Y 值total.beginText();// 生成的模版的字體、顏色total.setFontAndSize(bf, presentFontSize);//頁腳內容拼接 如 第1頁/共2頁//String foot2 = " " + (writer.getPageNumber()) + " 頁";//頁腳內容拼接 如 第1頁/共2頁String foot2 = String.valueOf(writer.getPageNumber() - 1);// 模版顯示的內容total.showText(foot2);total.endText();total.closePath();}
}
最后大致效果圖
靜態圖片文件存放位置項目下: