package com.yiboshi.science.utils; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import com.yiboshi.science.entity.SystemParameter; import com.yiboshi.science.enumeration.CommonEnum; import com.yiboshi.science.param.dto.*; import java.io.*; import java.util.Objects; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class ProjectInfoToPDF { /** * 生成项目申请书和合同书PDF * @param project 项目信息 * @return 生成的PDF文件字节数组 */ public static byte[] generateProjectPDF(ComProjectDTO project, List<SystemParameter> projAttributeList) throws DocumentException, IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Document document = new Document(PageSize.A4); PdfWriter writer = PdfWriter.getInstance(document, baos); try { // 文件水印 String watermark = project.getAppNo(); if (project.getProjState() >= CommonEnum.projState.pass.getCode()) { watermark = project.getProjNo(); } // 宋体 BaseFont bfChinese = loadChineseFont("simsun.ttc"); // 仿宋 BaseFont fsChinese = loadChineseFont("simfang.ttf"); // WINGDNG2 BaseFont WINGDNG2 = loadChineseFont("WINGDNG2.ttf"); // 添加水印 writer.setPageEvent(new WatermarkPageEvent(watermark, fsChinese)); document.open(); Font normalFont = new Font(bfChinese, 12, Font.NORMAL); Font boldFont = new Font(bfChinese, 12, Font.BOLD); //首页内容 FirstPageInfo(document, project, bfChinese, fsChinese); // 添加新页面 document.newPage(); // 添加项目内容章节 addSection(document, "一、项目基本情况", boldFont); addProjectBasicTable(document, project, bfChinese, fsChinese); // 添加项目基本信息表格 // 添加横向页面 document.setPageSize(PageSize.A4.rotate()); document.newPage(); // 添加项目组主要成员表格 addProjectMembersTable(document, project, bfChinese, fsChinese); document.newPage(); // 添加项目绩效目标表 addProjectKPITable(document, project, projAttributeList, bfChinese, fsChinese, WINGDNG2); // 恢复为纵向页面 document.setPageSize(PageSize.A4); document.newPage(); // 项目经费预算表 addProjectBudgetTable(document, project, bfChinese, fsChinese); // 插入申请书正文PDF if (null != project.getConvertUrl() && !project.getConvertUrl().trim().isEmpty()) { InsertPdfContent(document, "申请书正文", System.getProperty("user.dir") + project.getConvertUrl(), writer); } // 加载项目组成员简历 List<ComProjectMembersDTO> MemberList = project.getMembers(); if (null != MemberList && MemberList.size() > 0) { LoadProjectMember(document, MemberList, writer); } // 添加新页面 document.newPage(); addSection(document, "二、主要技术指标", boldFont); addContent(document, project.getTechnologyTarget(), normalFont); addSection(document, "三、主要经济指标", boldFont); addContent(document, project.getEconomyTarget(), normalFont); addSection(document, "四、项目实施中形成的示范基地、中试线、生产线及其规模等", boldFont); addContent(document, project.getAchievementTarget(), normalFont); addSection(document, "五、科技报告考核指标", boldFont); addContent(document, project.getTechnologyReportsTarget(), normalFont); addSection(document, "六、其他应考核的指标", boldFont); addContent(document, project.getOtherTarget(), normalFont); // 添加签字栏 addSignatureSection(document, normalFont); document.close(); return baos.toByteArray(); } finally { if (document.isOpen()) { document.close(); } baos.close(); } } /** * 加载中文字体 * @param fontName 字体文件名 * @return BaseFont 中文字体 * @throws RuntimeException 加载字体失败时抛出异常 */ private static BaseFont loadChineseFont(String fontName) { try { if ("linux".equals(getCurrentOperatingSystem())) { if (fontName.toLowerCase().endsWith(".ttc")) return BaseFont.createFont("/usr/share/fonts/" + fontName + ",0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); else return BaseFont.createFont("/usr/share/fonts/" + fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } else { if (fontName.toLowerCase().endsWith(".ttc")) return BaseFont.createFont("c:/Windows/Fonts/" + fontName + ",0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); else return BaseFont.createFont("c:/Windows/Fonts/" + fontName, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); } // // 从资源文件夹加载字体 // String path = ProjectInfoToPDF.class.getResource(fontPath).getPath(); // // // 根据字体文件类型使用不同的加载方式 // if (fontPath.toLowerCase().endsWith(".ttc")) { // return BaseFont.createFont(path + ",0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // } else { // return BaseFont.createFont(path, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // } } catch (Exception e) { throw new RuntimeException("加载字体文件失败,请确保系统中存在字体文件 " + fontName + ":" + e.getMessage(), e); } } public static String getCurrentOperatingSystem() { String os = System.getProperty("os.name").toLowerCase(); //System.out.println("---------当前操作系统是-----------" + os); return os; } /** * 首页项目信息 * @param document * @param project * @param bfChinese * @param fsChinese * @return * @throws DocumentException */ private static Document FirstPageInfo(Document document, ComProjectDTO project, BaseFont bfChinese, BaseFont fsChinese) throws DocumentException { Font titleFont = new Font(bfChinese, 20, Font.BOLD); Font normalFont = new Font(bfChinese, 12, Font.NORMAL); Font labelFont = new Font(fsChinese, 16, Font.NORMAL); Font valueFont = new Font(bfChinese, 16, Font.NORMAL); // 添加项目编号到左上角 Paragraph projNo; if (Objects.nonNull(project.getProjNo())) { Chunk text = new Chunk("项目编号:", normalFont); Chunk number = new Chunk(project.getProjNo(), normalFont); number.setUnderline(0.1f, -2f); // 设置下划线 projNo = new Paragraph(); projNo.add(text); projNo.add(number); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); } else { Chunk text = new Chunk("项目编号:", normalFont); Chunk underline = new Chunk(" ", normalFont); underline.setUnderline(0.1f, -2f); // 设置下划线 projNo = new Paragraph(); projNo.add(text); projNo.add(underline); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); projNo.add(Chunk.NEWLINE); } projNo.setAlignment(Element.ALIGN_LEFT); projNo.setSpacingAfter(50f); document.add(projNo); // 添加标题 Paragraph title = new Paragraph("省级临床医学中心科研项目申请书", titleFont); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(50f); // 增加标题后的间距 document.add(title); // 添加项目基本信息表格 PdfPTable table = new PdfPTable(2); table.setWidthPercentage(95); table.setSpacingBefore(50f); // 增加表格前的间距 table.setSpacingAfter(10f); table.setHorizontalAlignment(Element.ALIGN_LEFT); // 设置表格左对齐 // 设置列宽度 float[] columnWidths = {0.35f, 0.65f}; table.setWidths(columnWidths); // 添加表格内容 addTableRow(table, "项目名称:", project.getProjName(), labelFont, valueFont); addTableRow(table, "申报单位:", project.getAppUnitName(), labelFont, valueFont); addTableRow(table, "推荐部门(丙方):", "", labelFont, valueFont); addTableRow(table, "项目负责人:", project.getAppPersonName(), labelFont, valueFont); addTableRow(table, "联系电话:", project.getMobile(), labelFont, valueFont); addTableRow(table, "起止年限:", DateUtils.formatDateRange(project.getStartDate(), 1) + "至" + DateUtils.formatDateRange(project.getEndDate(), 1), labelFont, valueFont); addTableRow(table, "填报日期:", DateUtils.formatDateRange(project.getCreated(), 2), labelFont, valueFont); document.add(table); return document; } /** * 添加首页项目表格信息 * @param table * @param label * @param value * @param labelFont * @param valueFont */ private static void addTableRow(PdfPTable table, String label, String value, Font labelFont, Font valueFont) { // 标签单元格 PdfPCell labelCell = new PdfPCell(new Phrase(label, labelFont)); labelCell.setMinimumHeight(28f); labelCell.setVerticalAlignment(Element.ALIGN_MIDDLE); labelCell.setHorizontalAlignment(Element.ALIGN_RIGHT); labelCell.setPaddingRight(5f); labelCell.setBorder(Rectangle.NO_BORDER); // 值单元格 Phrase valuePhrase; if (value != null && !value.trim().isEmpty()) { valuePhrase = new Phrase(value, valueFont); } else { valuePhrase = new Phrase(" ", valueFont); // 空值时使用一个空格 } PdfPCell valueCell = new PdfPCell(valuePhrase); valueCell.setMinimumHeight(28f); valueCell.setVerticalAlignment(Element.ALIGN_MIDDLE); valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); // 修改为左对齐 valueCell.setPaddingLeft(5f); valueCell.setPaddingRight(10f); valueCell.setBorder(Rectangle.NO_BORDER); // 添加自定义事件来绘制下划线 valueCell.setCellEvent(new PdfPCellEvent() { public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) { PdfContentByte cb = canvases[PdfPTable.LINECANVAS]; cb.setLineWidth(0.5f); float y = position.getBottom() + 2; // 调整下划线位置 cb.moveTo(position.getLeft(), y); cb.lineTo(position.getRight(), y); cb.stroke(); } }); table.addCell(labelCell); table.addCell(valueCell); } /** * 项目基本信息 * @param document * @param project * @param bfChinese * @param fsChinese * @throws DocumentException */ private static void addProjectBasicTable(Document document, ComProjectDTO project, BaseFont bfChinese, BaseFont fsChinese) throws DocumentException { Font labelFont = new Font(bfChinese, 12, Font.NORMAL); Font valueFont = new Font(fsChinese, 12, Font.NORMAL); Font titleFont = new Font(bfChinese, 12, Font.BOLD); // 创建一个大表格 PdfPTable mainTable = new PdfPTable(1); // 单列表格 mainTable.setWidthPercentage(95); mainTable.setSpacingBefore(10f); // 申请人信息部分 PdfPTable personTable = new PdfPTable(9); // 9列,第一列用于标题 float[] personWidths = {1f, 1.5f, 2f, 1f, 2f, 1.5f, 2f, 2f, 2f}; // 调整第一列宽度 personTable.setWidths(personWidths); // 申请人信息标题行 PdfPCell titleCell = new PdfPCell(new Phrase("申\n请\n人\n信\n息", titleFont)); titleCell.setRowspan(6); // 合并6行 titleCell.setHorizontalAlignment(Element.ALIGN_CENTER); titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setMinimumHeight(150f); // 调整高度以适应内容 personTable.addCell(titleCell); // 第一行 addBasicTableCell(personTable, "姓 名", project.getAppPersonName(), 2, labelFont, valueFont); addBasicTableCell(personTable, "性别", project.getSex(), 2, labelFont, valueFont); addBasicTableCell(personTable, "出生年月", DateUtils.formatDateRange(project.getBirthday(), 1), 2, labelFont, valueFont); addBasicTableCell(personTable, "民族", project.getNationName(), 2, labelFont, valueFont); // 第二行 addBasicTableCell(personTable, "学 位", project.getDegreeName(), 2, labelFont, valueFont); addBasicTableCell(personTable, "职称", project.getTitleName(), 2, labelFont, valueFont); addBasicTableCell(personTable, "每年工作时间(月)", project.getJobTime().toString(), 4, labelFont, valueFont); // 第三行 addBasicTableCell(personTable, "电 话", project.getMobile(), 4, labelFont, valueFont); addBasicTableCell(personTable, "电子邮箱", project.getEmail(), 4, labelFont, valueFont); // 第四行 addBasicTableCell(personTable, "个人通讯地址", project.getAddress(), 8, labelFont, valueFont); // 第五行 addBasicTableCell(personTable, "工作单位", project.getAppUnitName(), 8, labelFont, valueFont); // 第六行 addBasicTableCell(personTable, "主要研究领域", project.getMainResearchAreas(), 8, labelFont, valueFont); // 将申请人信息表格添加到主表格 PdfPCell personCell = new PdfPCell(personTable); personCell.setPadding(0); personCell.setBorder(Rectangle.BOX); mainTable.addCell(personCell); // 申请单位信息部分 PdfPTable unitTable = new PdfPTable(5); // 5列,第一列用于标题 float[] unitWidths = {1.2f, 3f, 5f, 3f, 5f}; // 调整所有列的比例,保持第一列宽度一致 unitTable.setWidths(unitWidths); // 申请单位信息标题行 titleCell = new PdfPCell(new Phrase("申\n请\n单\n位\n信\n息", titleFont)); titleCell.setRowspan(5); // 合并5行 titleCell.setHorizontalAlignment(Element.ALIGN_CENTER); titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setMinimumHeight(100f); unitTable.addCell(titleCell); // 单位信息内容 addBasicTableCell(unitTable, "单位名称", project.getAppUnitName(), 4, labelFont, valueFont); addBasicTableCell(unitTable, "联系人", project.getUnitLinkName(), 2, labelFont, valueFont); addBasicTableCell(unitTable, "电话", project.getUnitLinkMobile(), 2, labelFont, valueFont); addBasicTableCell(unitTable, "电子信箱", project.getUnitLinkEmail(), 2, labelFont, valueFont); addBasicTableCell(unitTable, "传真", project.getUnitLinkFax(), 2, labelFont, valueFont); // 将申请单位信息表格添加到主表格 PdfPCell unitCell = new PdfPCell(unitTable); unitCell.setPadding(0); unitCell.setBorder(Rectangle.BOX); mainTable.addCell(unitCell); // 项目基本情况部分 PdfPTable projTable = new PdfPTable(5); // 5列,第一列用于标题 float[] projWidths = {1.2f, 3f, 5f, 3f, 5f}; // 与申请单位信息表格使用相同的比例 projTable.setWidths(projWidths); // 项目基本情况标题行 titleCell = new PdfPCell(new Phrase("项\n目\n基\n本\n信\n息", titleFont)); titleCell.setRowspan(8); // 合并8行 titleCell.setHorizontalAlignment(Element.ALIGN_CENTER); titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setMinimumHeight(200f); projTable.addCell(titleCell); // 项目基本情况内容 addBasicTableCell(projTable, "项目名称", project.getProjName(), 4, labelFont, valueFont); addBasicTableCell(projTable, "学科代码", project.getKnowledgeName(), 2, labelFont, valueFont); addBasicTableCell(projTable, "选题范围", project.getSubjectScope(), 2, labelFont, valueFont); addBasicTableCell(projTable, "项目类别", project.getProjClassName(), 2, labelFont, valueFont); addBasicTableCell(projTable, "研究期限", DateUtils.formatDateRange(project.getStartDate(), 1), 2, labelFont, valueFont); addBasicTableCell(projTable, "项目总经费(万元)", project.getTotalFunding().toString(), 2, labelFont, valueFont); addBasicTableCell(projTable, "申请经费", project.getGovFunding().toString(), 2, labelFont, valueFont); addBasicTableCell(projTable, "项目摘要", project.getProjAbstract(), 4, labelFont, valueFont); addBasicTableCell(projTable, "关键词", project.getProjKeywords(), 4, labelFont, valueFont); // 将项目基本情况表格添加到主表格 PdfPCell projCell = new PdfPCell(projTable); projCell.setPadding(0); projCell.setBorder(Rectangle.BOX); mainTable.addCell(projCell); document.add(mainTable); } /** * 添加基本信息表格单元格 * @param table * @param label * @param value * @param colspan * @param labelFont * @param valueFont */ private static void addBasicTableCell(PdfPTable table, String label, String value, int colspan, Font labelFont, Font valueFont) { PdfPCell labelCell = new PdfPCell(new Phrase(label, labelFont)); labelCell.setMinimumHeight(25f); labelCell.setVerticalAlignment(Element.ALIGN_MIDDLE); labelCell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(labelCell); PdfPCell valueCell = new PdfPCell(new Phrase(value != null ? value : "", valueFont)); valueCell.setMinimumHeight(25f); valueCell.setVerticalAlignment(Element.ALIGN_MIDDLE); valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); valueCell.setColspan(colspan - 1); table.addCell(valueCell); } /** * 添加项目组主要成员表格 * @param document PDF文档 * @param project 项目信息 * @param bfChinese 宋体字体 * @param fsChinese 仿宋字体 * @throws DocumentException */ private static void addProjectMembersTable(Document document, ComProjectDTO project, BaseFont bfChinese, BaseFont fsChinese) throws DocumentException { Font titleFont = new Font(bfChinese, 12, Font.BOLD); Font contentFont = new Font(bfChinese, 12, Font.NORMAL); // 添加表格标题 Paragraph title = new Paragraph("项目组主要成员", titleFont); title.add(new Chunk(" (注:不包括项目申请人)", contentFont)); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(10f); document.add(title); // 创建表格 PdfPTable table = new PdfPTable(12); // 12列 table.setWidthPercentage(98); // 设置列宽度比例 float[] widths = {0.5f, 1f, 1f, 0.8f, 0.8f, 0.8f, 2f, 0.8f, 1.2f, 1f, 1.2f, 1f}; table.setWidths(widths); // 添加表头 String[] headers = {"序号", "姓名", "出生年月", "性别", "职称", "学位", "工作单位", "电话", "电子邮箱", "证件号码", "项目分工", "每年工作时间(月)"}; for (String header : headers) { PdfPCell cell = new PdfPCell(new Phrase(header, contentFont)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setVerticalAlignment(Element.ALIGN_MIDDLE); cell.setMinimumHeight(25f); table.addCell(cell); } List<ComProjectMembersDTO> members = project.getMembers(); // 添加成员数据 if (members != null && !members.isEmpty()) { int index = 1; for (ComProjectMembersDTO member : members) { // 序号 addCenterCell(table, String.valueOf(index++), contentFont); // 姓名 addCenterCell(table, member.getName(), contentFont); // 出生年月 addCenterCell(table, DateUtils.formatDateRange(member.getBirthday(), 1), contentFont); // 性别 addCenterCell(table, member.getSex(), contentFont); // 职称 addCenterCell(table, member.getTitleName(), contentFont); // 学位 addCenterCell(table, member.getDegreeName(), contentFont); // 工作单位 addCenterCell(table, member.getWorkUnit(), contentFont); // 电话 addCenterCell(table, member.getMobile(), contentFont); // 电子邮箱 addCenterCell(table, member.getEmail(), contentFont); // 证件号码 addCenterCell(table, member.getCertId(), contentFont); // 项目分工 addCenterCell(table, member.getProjWork(), contentFont); // 每年工作时间 addCenterCell(table, member.getForMonths().toString(), contentFont); } } // 如果成员数量少于9个,添加空行到9行 int emptyRows = 9 - (members != null ? members.size() : 0); for (int i = 0; i < emptyRows; i++) { for (int j = 0; j < 12; j++) { addCenterCell(table, "", contentFont); } } document.add(table); // 项目组成员统计表格 PdfPTable statsTable = new PdfPTable(7); // 7列 statsTable.setWidthPercentage(98); statsTable.setSpacingBefore(20f); // 与上表格保持间距 // 设置列宽度比例 float[] statsWidths = {1f, 1f, 1f, 1f, 1f, 1f, 1f}; statsTable.setWidths(statsWidths); // 添加表头 String[] statsHeaders = {"总人数", "高级", "中级", "初级", "博士后", "博士生", "硕士生"}; for (String header : statsHeaders) { PdfPCell cell = new PdfPCell(new Phrase(header, contentFont)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setVerticalAlignment(Element.ALIGN_MIDDLE); cell.setMinimumHeight(25f); statsTable.addCell(cell); } // 添加数据行 addCenterCell(statsTable, project.getMemCount() != null ? project.getMemCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemHighCount() != null ? project.getMemHighCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemMiddleCount() != null ? project.getMemMiddleCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemLowCount() != null ? project.getMemLowCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemBshCount() != null ? project.getMemBshCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemBsCount() != null ? project.getMemBsCount().toString() : "", contentFont); addCenterCell(statsTable, project.getMemSsCount() != null ? project.getMemSsCount().toString() : "", contentFont); document.add(statsTable); } /** * 添加项目绩效目标表 * @param document PDF文档 * @param project 项目信息 * @param bfChinese 宋体字体 * @param fsChinese 仿宋字体 * @throws DocumentException */ private static void addProjectKPITable(Document document, ComProjectDTO project, List<SystemParameter> projAttributeList, BaseFont bfChinese, BaseFont fsChinese, BaseFont WINGDNG2) throws DocumentException, IOException { ProjectKPIStatisticDTO kpitDTO = project.getProjectKPI(); List<ComProjectKpitDTO> detail = project.getProjectKPI().getThreeLevel(); Font titleFont = new Font(bfChinese, 12, Font.BOLD); Font contentFont = new Font(bfChinese, 12, Font.NORMAL); Font wingdng2Font = new Font(WINGDNG2, 12, Font.NORMAL); // 添加表格标题 Paragraph title = new Paragraph("绩效目标表", titleFont); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(10f); document.add(title); // 创建主表格 PdfPTable mainTable = new PdfPTable(5); mainTable.setWidthPercentage(98); float[] widths = {0.8f, 1f, 2f, 1f, 2f}; mainTable.setWidths(widths); // 添加年度标题 PdfPCell yearCell = new PdfPCell(new Phrase(kpitDTO.getReportYear() + "年度", titleFont)); yearCell.setColspan(5); yearCell.setHorizontalAlignment(Element.ALIGN_CENTER); yearCell.setMinimumHeight(25f); mainTable.addCell(yearCell); // 项目名称 addLabelValueRow(mainTable, "项目名称", kpitDTO.getProjName(), 5, titleFont, contentFont); // 主管处室和实施单位 addLabelValueRow(mainTable, "主管处室", kpitDTO.getManagerDept(), 2, titleFont, contentFont); addLabelValueRow(mainTable, "实施单位", kpitDTO.getAppUnitName(), 1, titleFont, contentFont); // 项目属性 PdfPCell labelCell = new PdfPCell(new Phrase("项目属性", titleFont)); labelCell.setMinimumHeight(25f); labelCell.setHorizontalAlignment(Element.ALIGN_CENTER); mainTable.addCell(labelCell); // 创建复选框和文字组合 Phrase checkboxPhrase = new Phrase(); if (Objects.nonNull(kpitDTO.getProjAttribute())) { for (SystemParameter param : projAttributeList) { if (param.getId().toLowerCase().equals(kpitDTO.getProjAttribute().toLowerCase())) { checkboxPhrase.add(new Chunk("R", wingdng2Font)); // 选中状态 } else { checkboxPhrase.add(new Chunk("£", wingdng2Font)); // 未选中状态 } checkboxPhrase.add(new Chunk(" " + param.getName() + " ", contentFont)); // 使用中文字体显示文字 } } else { checkboxPhrase.add(new Chunk("£", wingdng2Font)); checkboxPhrase.add(new Chunk(" 新增项目 ", contentFont)); checkboxPhrase.add(new Chunk("£", wingdng2Font)); checkboxPhrase.add(new Chunk(" 延续项目", contentFont)); } PdfPCell valueCell = new PdfPCell(checkboxPhrase); valueCell.setMinimumHeight(25f); valueCell.setColspan(2); mainTable.addCell(valueCell); // 项目期 addLabelValueRow(mainTable, "项目期", kpitDTO.getProjDeadline(), 1, titleFont, contentFont); // 项目资金(万元)标题行 PdfPCell fundTitle = new PdfPCell(new Phrase("项目资金\n(万元)", titleFont)); fundTitle.setRowspan(3); fundTitle.setVerticalAlignment(Element.ALIGN_MIDDLE); fundTitle.setHorizontalAlignment(Element.ALIGN_CENTER); mainTable.addCell(fundTitle); // 总体资金总额和年度资金总额 addLabelValueRow(mainTable, "总体资金总额", kpitDTO.getTotalBudget().toString(), 1, titleFont, contentFont); addLabelValueRow(mainTable, "年度资金总额", kpitDTO.getYearTotal().toString(), 1, titleFont, contentFont); // 其中:财政拨款(两行) addLabelValueRow(mainTable, "其中:财政拨款", kpitDTO.getApplyFunds().toString(), 1, titleFont, contentFont); addLabelValueRow(mainTable, "其中:财政拨款", kpitDTO.getYearApply().toString(), 1, titleFont, contentFont); // 其他资金(两行) addLabelValueRow(mainTable, "其他资金", kpitDTO.getSelfFunds().toString(), 1, titleFont, contentFont); addLabelValueRow(mainTable, "其他资金", kpitDTO.getYearSelf().toString(), 1, titleFont, contentFont); document.add(mainTable); // 总体考核目标 PdfPTable middleTable = new PdfPTable(5); middleTable.setWidthPercentage(98); float[] middleWidths = {0.8f, 3.2f, 1f, 1f, 1f}; middleTable.setWidths(middleWidths); // 添加左侧标题单元格 PdfPCell titleCell = new PdfPCell(new Phrase("总体考核\n目标", titleFont)); titleCell.setRowspan(3); // 合并三行 titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setHorizontalAlignment(Element.ALIGN_CENTER); middleTable.addCell(titleCell); // 添加总体目标标题行 int startYear = DateUtils.getDateYear(kpitDTO.getStartDate()); int endYear = DateUtils.getDateYear(kpitDTO.getEndDate()); PdfPCell totalTargetCell = new PdfPCell(new Phrase("总体目标(" + startYear + "年—" + endYear + "年)", titleFont)); totalTargetCell.setHorizontalAlignment(Element.ALIGN_CENTER); totalTargetCell.setMinimumHeight(25f); middleTable.addCell(totalTargetCell); // 添加分年度指标标题行 PdfPCell yearlyTitleCell = new PdfPCell(new Phrase("分年度指标", titleFont)); yearlyTitleCell.setColspan(3); // 合并三列 yearlyTitleCell.setHorizontalAlignment(Element.ALIGN_CENTER); yearlyTitleCell.setMinimumHeight(25f); middleTable.addCell(yearlyTitleCell); // 添加总体目标内容 PdfPCell totalTargetContentCell = new PdfPCell(new Phrase(kpitDTO.getYearTarget(), contentFont)); totalTargetContentCell.setRowspan(2); // 合并两行 totalTargetContentCell.setVerticalAlignment(Element.ALIGN_MIDDLE); totalTargetContentCell.setMinimumHeight(50f); // 设置较大的高度以容纳多行内容 middleTable.addCell(totalTargetContentCell); // 添加年度标题行 for (int i = 0; i < 3; i++) { PdfPCell yearCCell = new PdfPCell(new Phrase((startYear + i) + "年", titleFont)); yearCCell.setHorizontalAlignment(Element.ALIGN_CENTER); yearCCell.setVerticalAlignment(Element.ALIGN_MIDDLE); yearCCell.setMinimumHeight(25f); middleTable.addCell(yearCCell); } // 添加年度内容 PdfPCell yearContentCell = new PdfPCell(new Phrase(kpitDTO.getYear1Goal(), contentFont)); yearContentCell.setMinimumHeight(50f); yearContentCell.setVerticalAlignment(Element.ALIGN_MIDDLE); middleTable.addCell(yearContentCell); yearContentCell = new PdfPCell(new Phrase(kpitDTO.getYear2Goal(), contentFont)); yearContentCell.setMinimumHeight(50f); yearContentCell.setVerticalAlignment(Element.ALIGN_MIDDLE); middleTable.addCell(yearContentCell); yearContentCell = new PdfPCell(new Phrase(kpitDTO.getYear3Goal(), contentFont)); yearContentCell.setMinimumHeight(50f); yearContentCell.setVerticalAlignment(Element.ALIGN_MIDDLE); middleTable.addCell(yearContentCell); document.add(middleTable); addTargetDetailTable(document, kpitDTO, titleFont, contentFont); } private static void addTargetDetailTable(Document document, ProjectKPIStatisticDTO kpitDTO, Font titleFont, Font contentFont) throws DocumentException { PdfPTable detailTable = new PdfPTable(11); detailTable.setWidthPercentage(98); float[] detailWidths = {0.5f, 0.5f, 0.5f, 1.5f, 0.5f, 0.5f, 0.5f, 1.5f, 0.5f, 0.5f, 0.5f}; detailTable.setWidths(detailWidths); //总考核目标 addTitleCell(detailTable, "总体考核目标", 0, kpitDTO.getTotalRowSpan(), titleFont); //一级指标 addTitleCell(detailTable, "一级指标", 0, 2, titleFont); //二级指标 addTitleCell(detailTable, "二级指标", 0, 2, titleFont); //三级指标 addTitleCell(detailTable, "三级指标", 0, 2, titleFont); //指标值 addTitleCell(detailTable, "指标值", 0, 2, titleFont); //绩效标准 addTitleCell(detailTable, "绩效标准", 0, 2, titleFont); //二级指标 addTitleCell(detailTable, "二级指标", 0, 2, titleFont); //三级指标 addTitleCell(detailTable, "三级指标", 0, 2, titleFont); //指标值 addTitleCell(detailTable, "指标值", 3, 0, titleFont); int startYear = DateUtils.getDateYear(kpitDTO.getStartDate()); addTitleCell(detailTable, startYear + "年", 0, 0, contentFont); addTitleCell(detailTable, (startYear + 1) + "年", 0, 0, contentFont); addTitleCell(detailTable, (startYear + 2) + "年", 0, 0, contentFont); List<ComProjectKpitDTO> list = kpitDTO.getThreeLevel(); for (int i = 0; i < list.size(); i++) { ComProjectKpitDTO model = list.get(i); if (model.getOneDisplay()) addTitleCell(detailTable, model.getOneLevelName(), 0, model.getOneRowSpan(), contentFont); if (model.getTowDisplay()) addTitleCell(detailTable, model.getTowLevelName(), 0, model.getTowRowSpan(), contentFont); addTitleCell(detailTable, model.getKpitName(), 0, 0, contentFont); addTitleCell(detailTable, model.getTargetValue() != null ? model.getTargetValue().toString() : "", 0, 0, contentFont); addTitleCell(detailTable, model.getPerformanceStandard() != null ? model.getPerformanceStandard().toString() : "", 0, 0, contentFont); if (model.getTowDisplay()) addTitleCell(detailTable, model.getTowLevelName(), 0, model.getTowRowSpan(), contentFont); addTitleCell(detailTable, model.getKpitName(), 0, 0, contentFont); addTitleCell(detailTable, model.getYearValue1() != null ? model.getYearValue1().toString() : "", 0, 0, contentFont); addTitleCell(detailTable, model.getYearValue2() != null ? model.getYearValue2().toString() : "", 0, 0, contentFont); addTitleCell(detailTable, model.getYearValue3() != null ? model.getYearValue3().toString() : "", 0, 0, contentFont); } document.add(detailTable); } private static void addTitleCell(PdfPTable detailTable, String title, int colSpan, int rowSpan, Font contentFont) { PdfPCell titleCell = new PdfPCell(new Phrase(title, contentFont)); if (colSpan > 1) titleCell.setColspan(colSpan); if (rowSpan > 1) titleCell.setRowspan(rowSpan); titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setHorizontalAlignment(Element.ALIGN_CENTER); detailTable.addCell(titleCell); } /** * 项目经费预算表 * @param document * @param project * @param bfChinese * @param fsChinese * @throws DocumentException */ private static void addProjectBudgetTable(Document document, ComProjectDTO project, BaseFont bfChinese, BaseFont fsChinese) throws DocumentException { Font titleFont = new Font(bfChinese, 12, Font.BOLD); Font contentFont = new Font(bfChinese, 12, Font.NORMAL); Font detailFont = new Font(fsChinese, 9, Font.NORMAL); // 添加表格标题 Paragraph title = new Paragraph("项目经费预算表", titleFont); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(10f); document.add(title); // 添加金额单位说明 Paragraph unit = new Paragraph("金额单位:万元(保留两位小数)", detailFont); unit.setAlignment(Element.ALIGN_RIGHT); unit.setSpacingAfter(10f); document.add(unit); // 创建预算表格 PdfPTable budgetTable = new PdfPTable(5); budgetTable.setWidthPercentage(98); float[] widths = {2f, 0.5f, 0.5f, 0.5f, 1f}; budgetTable.setWidths(widths); // 添加表头 String[] headers = {"预算科目", "总预算数", "财政资金", "自筹资金", "备注"}; for (String header : headers) { addTitleCell(budgetTable, header, 0, 0, titleFont); } for (ComProjectBudgetDTO dto : project.getBudget()) { if (dto.getBudgetId().toLowerCase().equals("3b1f57d3-6aec-4129-aef5-702a1accfe01") || dto.getBudgetId().toLowerCase().equals("3b1f57d3-6aec-4129-aef5-702a1accfe08")) addBudgetTitleCell(budgetTable, dto.getBudgetName(), titleFont, Element.ALIGN_LEFT, 0, 0); else addBudgetTitleCell(budgetTable, dto.getBudgetName(), contentFont, Element.ALIGN_LEFT, 0, 0); addBudgetTitleCell(budgetTable, dto.getTotalBudget().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getApplyFunds().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getSelfFunds().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getCalculationBasis(), contentFont, Element.ALIGN_LEFT, 0, 0); } addBudgetTitleCell(budgetTable, "三、分年度用款计划", titleFont, Element.ALIGN_LEFT, 5, 0); addBudgetTitleCell(budgetTable, "年度", titleFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, "第一年", titleFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, "第二年", titleFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, "第三年", titleFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, "合计", titleFont, Element.ALIGN_CENTER, 0, 0); for (ComProjectFundPlanDTO dto : project.getFundPlan()) { addBudgetTitleCell(budgetTable, dto.getFundName(), titleFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getYearValue1().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getYearValue2().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getYearValue3().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); addBudgetTitleCell(budgetTable, dto.getTotalAmount().toString(), contentFont, Element.ALIGN_CENTER, 0, 0); } document.add(budgetTable); } private static void addBudgetTitleCell(PdfPTable table, String title, Font contentFont, int HorizontalAlignment, int colSpan, int rowSpan) { PdfPCell titleCell = new PdfPCell(new Phrase(title, contentFont)); if (colSpan > 1) titleCell.setColspan(colSpan); if (rowSpan > 1) titleCell.setRowspan(rowSpan); titleCell.setVerticalAlignment(Element.ALIGN_MIDDLE); titleCell.setHorizontalAlignment(HorizontalAlignment); table.addCell(titleCell); } /** * 添加标签值行 */ private static void addLabelValueRow(PdfPTable table, String label, String value, int valueColspan, Font titleFont, Font contentFont) { PdfPCell labelCell = new PdfPCell(new Phrase(label, titleFont)); labelCell.setMinimumHeight(25f); labelCell.setVerticalAlignment(Element.ALIGN_MIDDLE); labelCell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(labelCell); PdfPCell valueCell = new PdfPCell(new Phrase(value != null ? value : "", contentFont)); valueCell.setMinimumHeight(25f); valueCell.setVerticalAlignment(Element.ALIGN_MIDDLE); valueCell.setHorizontalAlignment(Element.ALIGN_LEFT); valueCell.setColspan(valueColspan); table.addCell(valueCell); } /** * 添加居中对齐的单元格 */ private static void addCenterCell(PdfPTable table, String content, Font font) { PdfPCell cell = new PdfPCell(new Phrase(content != null ? content : "", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setVerticalAlignment(Element.ALIGN_MIDDLE); cell.setMinimumHeight(25f); table.addCell(cell); } private static void addSection(Document document, String title, Font font) throws DocumentException { Paragraph section = new Paragraph(title, font); section.setSpacingBefore(15); section.setSpacingAfter(10); document.add(section); } private static void addContent(Document document, String content, Font font) throws DocumentException { if (content != null) { Paragraph paragraph = new Paragraph(content, font); paragraph.setIndentationLeft(20); paragraph.setSpacingAfter(10); document.add(paragraph); } } private static void addSignatureSection(Document document, Font font) throws DocumentException { Paragraph signature = new Paragraph("\n\n", font); signature.add(new Chunk("申请人签字:", font)); signature.add(new Chunk(" ", font)); signature.add(new Chunk("日期:", font)); signature.add(new Chunk(" ", font)); signature.setAlignment(Element.ALIGN_RIGHT); signature.setSpacingBefore(50); document.add(signature); Paragraph unitSignature = new Paragraph("\n", font); unitSignature.add(new Chunk("申请单位(盖章):", font)); unitSignature.add(new Chunk(" ", font)); unitSignature.add(new Chunk("日期:", font)); unitSignature.add(new Chunk(" ", font)); unitSignature.setAlignment(Element.ALIGN_RIGHT); unitSignature.setSpacingBefore(30); document.add(unitSignature); } /** * 水印页面事件 */ private static class WatermarkPageEvent extends PdfPageEventHelper { private String watermarkText; private BaseFont baseFont; public WatermarkPageEvent(String watermarkText, BaseFont baseFont) { this.watermarkText = watermarkText; this.baseFont = baseFont; } @Override public void onEndPage(PdfWriter writer, Document document) { try { PdfContentByte canvas = writer.getDirectContentUnder(); Rectangle pageSize = document.getPageSize(); float width = pageSize.getWidth(); float height = pageSize.getHeight(); // 设置水印字体 canvas.saveState(); canvas.beginText(); canvas.setFontAndSize(baseFont, 30); // 减小字体大小 canvas.setGrayFill(0.9f); // 计算水印间距 float xStep = width / 2; // 水平间距 float yStep = height / 3; // 垂直间距 // 在页面上添加多个水印 for (float y = yStep/2; y < height; y += yStep) { for (float x = xStep/2; x < width; x += xStep) { canvas.showTextAligned(Element.ALIGN_CENTER, watermarkText, x, y, 45); } } canvas.endText(); canvas.restoreState(); // 添加页码 PdfContentByte canvasOver = writer.getDirectContent(); canvasOver.saveState(); canvasOver.beginText(); canvasOver.setFontAndSize(baseFont, 12); // 设置页码字体大小 canvasOver.setColorFill(BaseColor.BLACK); // 页码文本 String text = String.format("第 %d 页", writer.getPageNumber()); // 在页面底部居中添加页码 canvasOver.showTextAligned(Element.ALIGN_CENTER, text, width / 2, // 页面中心 15, // 距离底部15单位,降低页码位置 0); // 不旋转 canvasOver.endText(); canvasOver.restoreState(); } catch (Exception e) { e.printStackTrace(); } } } /** * 插入PDF文件到指定位置 * @param document 目标文档 * @param titleName 标题名称 * @param pdfPath 要插入的PDF文件路径 * @throws DocumentException * @throws IOException */ private static void InsertPdfContent(Document document, String titleName, String pdfPath, PdfWriter writer) throws DocumentException, IOException { PdfReader reader = null; try { if (Objects.nonNull(titleName)) { // 添加标题 Font titleFont = new Font(BaseFont.createFont(), 12, Font.BOLD); Paragraph title = new Paragraph(titleName, titleFont); title.setAlignment(Element.ALIGN_CENTER); title.setSpacingAfter(20f); document.add(title); } // 读取PDF文件内容 byte[] pdfBytes = readPdfToBytes(pdfPath); // 创建PdfReader reader = new PdfReader(pdfBytes); // 获取PDF页数 int totalPages = reader.getNumberOfPages(); // 复制每一页 for (int i = 1; i <= totalPages; i++) { document.newPage(); PdfImportedPage page = writer.getImportedPage(reader, i); PdfContentByte cb = writer.getDirectContent(); cb.addTemplate(page, 0, 0); } } catch (Exception e) { System.out.println("插入PDF文件失败: " + e.getMessage()); e.printStackTrace(); throw e; // 重新抛出异常以确保错误被正确处理 } // 不在这里关闭reader,让它在document关闭时自动关闭 } /** * 加载项目组成员简历 * @param document PDF文档 * @param MemberList 成员列表 * @param writer PDF写入器 */ private static void LoadProjectMember(Document document, List<ComProjectMembersDTO> MemberList, PdfWriter writer) throws DocumentException, IOException { // 遍历每个成员的简历 for (ComProjectMembersDTO member : MemberList) { if (Objects.nonNull(member.getFileId())) { InsertPdfContent(document, "", System.getProperty("user.dir") + member.getConvertUrl(), writer); } } } /** * 读取PDF文件并返回字节数组 * @param pdfPath PDF文件路径 * @return 字节数组 */ private static byte[] readPdfToBytes(String pdfPath) throws IOException { File file = new File(pdfPath); if (!file.exists()) { throw new FileNotFoundException("PDF文件不存在: " + pdfPath); } try (FileInputStream fis = new FileInputStream(file)) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); } return bos.toByteArray(); } } }