diff --git a/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/form/study/StudyFormFillExportForm.java b/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/form/study/StudyFormFillExportForm.java new file mode 100644 index 0000000..4517799 --- /dev/null +++ b/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/form/study/StudyFormFillExportForm.java @@ -0,0 +1,61 @@ +package com.hxhq.business.form.study; + +/** + * @author tanfei + */ +public class StudyFormFillExportForm { + /** 表单id */ + private Long studyFormFillId; + + /** url */ + private String url; + + /** lang */ + private String lang; + + /** version */ + private String version; + + /** jcgjlx */ + private Integer jcgjlx; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getLang() { + return lang; + } + + public void setLang(String lang) { + this.lang = lang; + } + + public Integer getJcgjlx() { + return jcgjlx; + } + + public void setJcgjlx(Integer jcgjlx) { + this.jcgjlx = jcgjlx; + } + + public Long getStudyFormFillId() { + return studyFormFillId; + } + + public void setStudyFormFillId(Long studyFormFillId) { + this.studyFormFillId = studyFormFillId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/utils/StudyFormFillUtil.java b/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/utils/StudyFormFillUtil.java new file mode 100644 index 0000000..451bc7b --- /dev/null +++ b/hxhq-modules/hxhq-system/src/main/java/com/hxhq/business/utils/StudyFormFillUtil.java @@ -0,0 +1,467 @@ +package com.hxhq.business.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hxhq.business.domain.*; +import com.hxhq.business.utils.lang.TemplateUtil; +import com.hxhq.business.utils.pdf.PdfBaseUtil; +import com.hxhq.business.utils.pdf.PdfExportUtil; +import com.hxhq.common.core.text.Convert; +import com.hxhq.common.core.utils.DateUtils; +import com.hxhq.common.core.utils.StringUtils; +import com.hxhq.common.security.utils.SecurityUtils; +import com.hxhq.system.api.model.LoginUser; +import com.itextpdf.text.*; +import com.itextpdf.text.pdf.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import java.util.Random; +import java.util.StringJoiner; + +/** + * 试验方法pdf导出 + * + * @author HanLong + */ +@Component +public class StudyFormFillUtil { + + private static final Logger log = LoggerFactory.getLogger(StudyFormFillUtil.class); + public static String language = "zh"; + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + @Value("${file.domain}") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 复制原有PDF的所有页面到新Document中 + */ + private static void copyOriginalPagesToDocument(Document doc, PdfWriter writer, PdfReader reader) throws DocumentException { + int pageCount = reader.getNumberOfPages(); + // 获取直接内容画布 + PdfContentByte canvas = writer.getDirectContent(); + + for (int i = 1; i <= pageCount; i++) { + doc.newPage(); // 新建一页(与原PDF页面一一对应) + // 5.5.11中通过PdfImportedPage导入原有页面 + PdfImportedPage importedPage = writer.getImportedPage(reader, i); + // 将原有页面绘制到新文档的当前页 + canvas.addTemplate(importedPage, 0, 0); + } + } + + /** + * 导出table + * + * @param headerText + * @param fileDir + * @return + */ + public static String export(String version,String lang, String headerText, String srcPdf, String fileDir, List jcgjList, List qmxxList) { + language = lang; + Document document = null; + FileOutputStream fos = null; + String filePath = ""; + String fileName = ""; + try { + // 1. 生成文件名 + fileName = generateFileName(); + // 2. 确保目录存在 + File dir = new File(fileDir); + if (!dir.exists()) { + dir.mkdirs(); + } + // 3. 完整文件路径 + filePath = Paths.get(fileDir, fileName).toString(); + // 创建PDF文档 设置文档边距,避免内容遮挡页眉页脚 + // 1. 读取原有PDF,获取页面尺寸(保证新文档与原文档格式一致) + PdfReader srcReader = new PdfReader(srcPdf); + // 取第一页尺寸作为新文档尺寸 + Rectangle pageSize = srcReader.getPageSize(1); + // 2. 初始化Document(iText 5的核心高层类) + document = new Document(pageSize); + fos = new FileOutputStream(filePath); + // 3. 关联PdfWriter,绑定Document和输出流(5.5.11必须先创建Writer再open Document) + PdfWriter writer = PdfWriter.getInstance(document, fos); + // 设置页面事件,每页添加文字页眉 + String sign = "hxhq"; + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser != null) { + sign = loginUser.getSysUser().getNickName() + " " + DateUtils.getTime(); + } + writer.setPageEvent(new TextHeaderEvent(sign+" "+version, headerText, false)); + // 4. 设置PDF属性 + document.addTitle("华西海圻"); + document.addAuthor("华西海圻"); + document.addCreator("华西海圻"); + document.addCreationDate(); + document.open(); + // 5. 复制原有PDF的所有页面到新Document中 + copyOriginalPagesToDocument(document, writer, srcReader); + // 7. 表格设置到新的一页 + document.newPage(); + addQmxx(document, qmxxList, lang); + addJcgj(document, jcgjList, lang); + } catch (Exception e) { + throw new RuntimeException("生成失败: " + e.getMessage()); + } finally { + if (document != null) { + document.close(); + } + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + log.error("关闭文件流失败", e); + } + } + } + return fileName; + } + + /** + * 签名信息 + * 申请表单 + * + * @param document + * @param studyFormFillQmxxList + * @throws IOException + * @throws DocumentException + */ + public static void addQmxx(Document document, List studyFormFillQmxxList, String lang) throws IOException, DocumentException { + language = lang; + PdfBaseUtil.addUnderlinedTitle(document, getName("签名信息"), 10, true); + // 9. 表头 + BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); + Font headerFont = new Font(bfChinese, 10, Font.NORMAL); + Font contentFont = new Font(bfChinese, 10, Font.NORMAL); + + // 8. 创建表格 + PdfPTable table = new PdfPTable(4); + table.setWidthPercentage(100); + table.setSpacingBefore(10); + String[] headers = {getName("签名人"), getName("签名意义"), getName("签名时间"), getName("备注原因")}; + for (String header : headers) { + PdfPCell cell = new PdfPCell(new Phrase(header, headerFont)); + cell.setPadding(8); + cell.setHorizontalAlignment(Element.ALIGN_CENTER); + cell.setBackgroundColor(BaseColor.WHITE); + cell.setBorderWidth(1); + table.addCell(cell); + } + if (studyFormFillQmxxList.size() > 0) { + int rowNum = 0; + for (StudyFormFillQmxx studyFormFillQmxx : studyFormFillQmxxList) { + // 交替行颜色 + if (rowNum % 2 == 0) { + table.getDefaultCell().setBackgroundColor(BaseColor.WHITE); + } else { + table.getDefaultCell().setBackgroundColor(BaseColor.WHITE); + } + table.addCell(PdfBaseUtil.createCell(studyFormFillQmxx.getQmrMc(), contentFont)); + table.addCell(PdfBaseUtil.createCell("en".equals(language) ? studyFormFillQmxx.getQmyyEn() : studyFormFillQmxx.getQmyy(), contentFont)); + table.addCell(PdfBaseUtil.createCell(PdfExportUtil.parseDateToStr(studyFormFillQmxx.getCreateTime()), contentFont)); + table.addCell(PdfBaseUtil.createCell(studyFormFillQmxx.getRemark(), contentFont)); + rowNum++; + } + } + document.add(table); + } + + /** + * 稽查轨迹 + * 申请表单 + * + * @param document + * @param studyFormFillJcgjList + * @throws IOException + * @throws DocumentException + */ + public static void addJcgj(Document document, List studyFormFillJcgjList, String lang) throws IOException, DocumentException { + language = lang; + PdfBaseUtil.addUnderlinedTitle(document, getName("稽查轨迹"), 10, true); + for (StudyFormFillJcgj jcgj : studyFormFillJcgjList) { + StringJoiner result = new StringJoiner(", "); + if ("en".equals(lang)) { + PdfBaseUtil.addUnderlinedTitle(document, PdfExportUtil.parseDateToStr(jcgj.getCreateTime()) + " " + jcgj.getJcmcEn(), 10, false); + if (StringUtils.isNoneBlank(jcgj.getJcnrEn())) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonArray = mapper.readTree(jcgj.getJcnrEn()); + for (JsonNode node : jsonArray) { + if (node.get("name") != null) { + String name = node.get("name").asText(); + String value = node.get("value") != null ? node.get("value").asText() : ""; + result.add(name + ":" + value); + } + } + } + } else { + PdfBaseUtil.addUnderlinedTitle(document, PdfExportUtil.parseDateToStr(jcgj.getCreateTime()) + " " + jcgj.getJcmc(), 10, false); + if (StringUtils.isNoneBlank(jcgj.getJcnr())) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonArray = mapper.readTree(jcgj.getJcnr()); + for (JsonNode node : jsonArray) { + if (node.get("name") != null) { + String name = node.get("name").asText(); + String value = node.get("value") != null ? node.get("value").asText() : ""; + result.add(name + ":" + value); + } + } + } + } + + if (StringUtils.isNoneBlank(jcgj.getRemark())) { + result.add(getName("备注") + ":" + jcgj.getRemark()); + } + if (StringUtils.isNoneBlank(jcgj.getQmrMc())) { + result.add(getName("签名人") + ":" + jcgj.getQmrMc()); + } + PdfBaseUtil.addUnderlinedTitle(document, result.toString(), 10, false); + } + } + + + /** + * 获取名称 + * + * @param name + * @return + */ + public static String getName(String name) { + return "en".equals(language) ? TemplateUtil.getEn(name) : name; + } + + + /** + * 创建表格 + */ + private static PdfPCell createCell(String content, Font font) { + PdfPCell cell = new PdfPCell(new Phrase(content, font)); + cell.setHorizontalAlignment(Element.ALIGN_CENTER); + cell.setVerticalAlignment(Element.ALIGN_MIDDLE); + cell.setPadding(6); + cell.setMinimumHeight(25); + return cell; + } + + + /** + * 生成文件名 + */ + private static String generateFileName() { + Random random = new Random(); + String timestamp = DateUtils.dateTimeNow("yyyyMMddHHmmss"); + return String.format("%s_%s.pdf", timestamp, random.nextInt(1000)); + } + + + /** + * 页面事件处理类 - 每页添加文字页眉 + */ + static class TextHeaderEvent extends PdfPageEventHelper { + private Font footerFont; + private String signText; + private String headerText; + private BaseFont baseFont; + private Boolean showHaderLine = true; + private int totalPages = 0; + private PdfTemplate total; + + public TextHeaderEvent(String signText, String headerText, Boolean showHaderLine) throws Exception { + try { + this.showHaderLine = showHaderLine; + this.signText = signText; + this.headerText = headerText; + // 创建字体(支持中文) + this.baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); + footerFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.GRAY); + } catch (Exception e) { + footerFont = new Font(Font.FontFamily.HELVETICA, 10, Font.NORMAL, BaseColor.GRAY); + } + } + + @Override + public void onOpenDocument(PdfWriter writer, Document document) { + total = writer.getDirectContent().createTemplate(100, 16); + } + + @Override + public void onCloseDocument(PdfWriter writer, Document document) { + totalPages = writer.getPageNumber(); + String totalPagesStr = String.valueOf(totalPages); + + // 重新创建合适宽度的模板 + BaseFont baseFont = footerFont.getBaseFont(); + float actualWidth = baseFont.getWidthPoint(totalPagesStr, footerFont.getSize()); + + total.beginText(); + total.setFontAndSize(baseFont, footerFont.getSize()); + total.setTextMatrix(0, 0); + total.showText(totalPagesStr); + total.endText(); + } + + @Override + public void onEndPage(PdfWriter writer, Document document) { + try { + PdfContentByte cb = writer.getDirectContent(); + + // 获取页面尺寸 + Rectangle pageSize = document.getPageSize(); + float pageWidth = pageSize.getWidth(); + float pageHeight = pageSize.getHeight(); + + // 设置页眉参数 + float topMargin = 15; // 顶部边距 + float fontSize = 12; // 字体大小 + + // 计算文字宽度(用于居中) + float textWidth = baseFont.getWidthPoint(signText, fontSize); + + // 计算居中位置 + float textX = (pageSize.getLeft() + pageSize.getRight()) / 2; + float textY = pageHeight - topMargin; + + // 获取画布 + PdfContentByte canvas = writer.getDirectContent(); + + int currentPage = writer.getPageNumber(); + float y = document.bottom() - 20; + + // 在每一页都重新计算,确保位置准确 + String pageText = signText+ " 第 " + currentPage + " 页 / 共 "; + BaseFont baseFont = footerFont.getBaseFont(); + + // 计算页面文本宽度 + float pageTextWidth = baseFont.getWidthPoint(pageText, footerFont.getSize()); + + // 临时用估计的总页数计算位置(等文档关闭后会被替换) + String tempTotal = String.valueOf(writer.getPageNumber() + 5); // 估计值 + float totalWidth = baseFont.getWidthPoint(tempTotal, footerFont.getSize()); + + // 计算起始位置 + float totalTextWidth = pageTextWidth + totalWidth + + baseFont.getWidthPoint(" 页", footerFont.getSize()); + float startX = (document.getPageSize().getWidth() - totalTextWidth) / 2; + + // 写入当前页信息 + ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, + new Phrase(pageText, footerFont), startX, y, 0); + + // 添加总页数模板 + cb.addTemplate(total, startX + pageTextWidth, y); + + // 写入"页"字 + ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, + new Phrase(" 页", footerFont), startX + pageTextWidth + totalWidth, y, 0); + +// PdfContentByte content = writer.getDirectContentUnder(); // 在水印层添加 +// +// // 设置透明度 +// PdfGState gs = new PdfGState(); +// float opacity = Convert.toFloat("0.3"); +// gs.setFillOpacity(opacity); +// content.setGState(gs); +// +// // 设置字体和颜色 +// content.setColorFill(BaseColor.RED); +// content.setFontAndSize(baseFont, fontSize); +// +// float width = pageSize.getWidth(); +// float height = pageSize.getHeight(); +// +// // 重复铺满水印 +// addRepeatedWatermark(content, width, height, signText); + // 添加页眉文字 + if (StringUtils.isNoneBlank(headerText)) { + PdfContentByte cb2 = writer.getDirectContent(); + ColumnText.showTextAligned(cb2, Element.ALIGN_LEFT, + new Phrase(headerText, footerFont), startX, textY, 0); +// canvas.beginText(); +// canvas.setFontAndSize(baseFont, fontSize); +// canvas.setTextMatrix(textX, textY); +// canvas.showText(headerText); +// canvas.endText(); + if (showHaderLine) { + // 添加页眉分隔线(可选) + addHeaderLine(canvas, pageWidth, textY - 10); + } + } + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 添加水印 + * + * @param content + * @param width + * @param height + * @param signText + */ + private void addRepeatedWatermark(PdfContentByte content, float width, float height, String signText) { + // 计算文字尺寸 + float fontSize = Convert.toFloat("10"); + float textWidth = baseFont.getWidthPoint(signText, fontSize) + 50; + float textHeight = baseFont.getAscentPoint(signText, fontSize) - + baseFont.getDescentPoint(signText, fontSize); + + // 计算间距 + float stepX = textWidth + 70; + float stepY = textHeight + 100; + + // 添加重复水印 + Integer two = 2; + for (float x = textWidth / two; x < width; x += stepX) { + for (float y = textHeight / two; y < height; y += stepY) { + content.beginText(); + content.showTextAligned( + Element.ALIGN_CENTER, + signText, + x, + y, + fontSize + ); + content.endText(); + } + } + } + + /** + * 添加页眉线 + * + * @param canvas + * @param pageWidth + * @param yPos + */ + private void addHeaderLine(PdfContentByte canvas, float pageWidth, float yPos) { + canvas.setLineWidth(0.5f); + canvas.lineTo(pageWidth, yPos); + canvas.stroke(); + } + } +} \ No newline at end of file