入口类: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 {
// 单据id
String 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();
// 标题1
Paragraph 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);
// 标题2
Paragraph 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);
// 标题3
Paragraph 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);
// 标题4
Paragraph 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);
}
@Override
public 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 {
@Override
public 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 {
@Override
public 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;
}
/**
*
* 文档打开时创建模板
*/
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
// 共 页 的矩形的长宽高
total = writer.getDirectContent().createTemplate(50, 50);
}
/**
*
*关闭每页的时候,写入页眉,写入'第几页共'这几个字。
*/
@Override
public 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轴坐标,字体长度也要计算进去 = len
float len = bf.getWidthPoint(foot1, presentFontSize);
// 4.拿到当前的PdfContentByte
PdfContentByte cb = writer.getDirectContent();
// 5.写入页脚1,x轴就是(右margin+左margin + right() -left()- len)/2.0F
ColumnText
.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();
// }
// }
/**
*
* 关闭文档时,替换模板,完成整个页眉页脚组件
*/
@Override
public 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();
}
}
最后大致效果图
静态图片文件存放位置项目下: