package com.yiboshi.science.utils; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import com.yiboshi.science.param.dto.ComProjectDTO; import org.springframework.core.io.ClassPathResource; import java.io.*; import java.util.Objects; import java.text.SimpleDateFormat; import java.util.Date; public class ProjectInfoToPDF { /** * 生成项目申请书和合同书PDF * @param project 项目信息 * @param outputPath 输出路径 */ public static void generateProjectPDF(ComProjectDTO project, String outputPath) throws DocumentException, IOException { Document document = new Document(PageSize.A4); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPath)); document.open(); // 宋体 BaseFont bfChinese = loadChineseFont("/fonts/simsun.ttc"); // 仿宋 BaseFont fsChinese = loadChineseFont("/fonts/simfang.ttf"); 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.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(); } /** * 加载中文字体 * @param fontPath 字体文件路径(相对于resources目录) * @return BaseFont 中文字体 * @throws RuntimeException 加载字体失败时抛出异常 */ private static BaseFont loadChineseFont(String fontPath) { try { // 从资源文件夹加载字体 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("加载字体文件失败,请确保resources目录下存在字体文件 " + fontPath + ":" + e.getMessage(), e); } } /** * 格式化起止年限 * @param date 开始日期 * @return 格式化后的日期范围字符串 */ private static String formatDateRange(Date date, int type) { // 定义日期格式 SimpleDateFormat sdf; if (type == 1) sdf = new SimpleDateFormat("yyyy年MM月"); else sdf = new SimpleDateFormat("yyyy年MM月dd日"); return sdf.format(date); } /** * 首页项目信息 * @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, "起止年限:", formatDateRange(project.getStartDate(), 1) + "至" + formatDateRange(project.getEndDate(), 1), labelFont, valueFont); addTableRow(table, "填报日期:", 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, "出生年月", 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, "研究期限", 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); } 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); } }