package com.kingdee.eas.custom.compensation.utils; import com.google.common.collect.Maps; import com.kingdee.bos.BOSException; import com.kingdee.bos.Context; import com.kingdee.shr.base.syssetting.exception.SHRWebException; import com.kingdee.shr.compensation.util.columnModel.CmpColumnModels; import com.kingdee.shr.compensation.web.handler.integrate.entry.BaseSubmitBillEntryGenerator; import com.spire.doc.FileFormat; import org.apache.commons.lang3.StringUtils; import org.apache.poi.xwpf.usermodel.*; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @Description 模板工具类 * @Date 2025/9/16 18:08 * @Created by 59279 */ public class TemplateUtil { /** * 从Word文档中提取所有${xxx}格式的变量 * * @param filePath Word文档路径 * @return 包含所有变量的集合 * @throws IOException 如果读取文件失败 */ public static Set extractVariablesFromWord(String filePath) throws IOException { Set variables = new HashSet(); FileInputStream fis = null; XWPFDocument document = null; try { fis = new FileInputStream(filePath); document = new XWPFDocument(fis); // 处理表格中的变量 for (XWPFTable table : document.getTables()) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph paragraph : cell.getParagraphs()) { extractVariablesFromText(paragraph.getText(), variables); } } } } // 处理段落中的变量 for (XWPFParagraph paragraph : document.getParagraphs()) { extractVariablesFromText(paragraph.getText(), variables); } } finally { if (fis != null) { fis.close(); } if (document != null) { document.close(); } } return variables; } /** * 从Word文档中提取所有${xxx}格式的变量 * * @param file Word文档字节数组 * @return 包含所有变量的集合 * @throws IOException 如果读取文件失败 */ public static Set extractVariablesFromWord(byte[] file) throws IOException { Set variables = new LinkedHashSet(); ByteArrayInputStream bis = null; XWPFDocument document = null; try { bis = new ByteArrayInputStream(file); document = new XWPFDocument(bis); // 处理表格中的变量 for (XWPFTable table : document.getTables()) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph paragraph : cell.getParagraphs()) { extractVariablesFromText(paragraph.getText(), variables); } } } } // 处理段落中的变量 for (XWPFParagraph paragraph : document.getParagraphs()) { extractVariablesFromText(paragraph.getText(), variables); } } finally { if (bis != null) { bis.close(); } if (document != null) { document.close(); } } return variables; } /** * 从文本中提取变量并添加到集合中 * * @param text 要搜索的文本 * @param variables 变量集合 */ private static void extractVariablesFromText(String text, Set variables) { // 匹配${xxx}格式的正则表达式 Pattern pattern = Pattern.compile("\\$\\{([^}]+)\\}"); Matcher matcher = pattern.matcher(text); while (matcher.find()) { // 获取匹配的变量名(去掉${和}) String variable = matcher.group(1); variables.add(variable); } } /** * 处理Word模板,替换文本框变量并添加表格行 * * @param file * @param variables 文本框变量映射 * @param tableData 表格数据 * @param approveList 审批信息 * @throws IOException 如果读写文件失败 */ public static byte[] processWordTemplate( byte[] file, Map variables, List> tableData, List> approveList ) throws IOException, BOSException { ByteArrayInputStream bis = null; XWPFDocument document = null; try { bis = new ByteArrayInputStream(file); document = new XWPFDocument(bis); // 1. 处理文本框中的变量替换 processTextboxes(document, variables); // 2. 处理段落中的变量替换 processParagraphs(document, variables); // 3. 处理页眉页脚中的变量替换 processHeadersAndFooters(document, variables); List tables = document.getTables(); if (!tables.isEmpty()) { if (tables.size() > 0) { // 4. 处理表格 - 查找并添加行 processTables(tables.get(0), tableData); } if (tables.size() > 1) { // 5. 处理审批表格表格 - 查找并添加行 processApproveTables(tables.get(1), approveList); } } // 保存文档 ByteArrayOutputStream bos = null; ByteArrayOutputStream pdfBos = null; ByteArrayInputStream byteArrayInputStream = null; try { bos = new ByteArrayOutputStream(); document.write(bos); byteArrayInputStream = new ByteArrayInputStream(bos.toByteArray()); pdfBos = new ByteArrayOutputStream(); //实例化Document类的对象 com.spire.doc.Document doc = new com.spire.doc.Document(); doc.loadFromStream(byteArrayInputStream, FileFormat.Doc); //保存为PDF格式 doc.saveToStream(pdfBos, FileFormat.PDF); //去除水印 byte[] bytes = PDFUtil.removeWatermark(pdfBos.toByteArray(),"Evaluation Warning: The document was created with Spire.Doc for JAVA."); return bytes; } finally { if (bos != null) { bos.close(); } if (byteArrayInputStream != null) { byteArrayInputStream.close(); } if (pdfBos != null) { pdfBos.close(); } } } finally { if (bis != null) { bis.close(); } if (document != null) { document.close(); } } } /** * 处理文档中的所有文本框,替换变量 * * @param document Word文档对象 * @param variables 变量映射 */ private static void processTextboxes(XWPFDocument document, Map variables) { // 获取文档中的所有绘图对象(包括文本框) //List drawings = new ArrayList<>(); // 从段落中获取绘图对象 for (XWPFParagraph paragraph : document.getParagraphs()) { for (XWPFRun run : paragraph.getRuns()) { if (run.getEmbeddedPictures().size() > 0 || run.getCTR().getDrawingList().size() > 0) { // 这里可以处理包含绘图的运行 } } } // 从页眉获取绘图对象 List headers = document.getHeaderList(); for (XWPFHeader header : headers) { for (XWPFParagraph paragraph : header.getParagraphs()) { for (XWPFRun run : paragraph.getRuns()) { if (run.getCTR().getDrawingList().size() > 0) { // 处理页眉中的绘图 } } } } // 从页脚获取绘图对象 List footers = document.getFooterList(); for (XWPFFooter footer : footers) { for (XWPFParagraph paragraph : footer.getParagraphs()) { for (XWPFRun run : paragraph.getRuns()) { if (run.getCTR().getDrawingList().size() > 0) { // 处理页脚中的绘图 } } } } // 由于POI对文本框的直接支持有限,这里使用XML遍历方式处理文本框 try { // 获取文档的底层XML对象 XmlCursor cursor = document.getDocument().newCursor(); // 移动到文档开始 cursor.toStartDoc(); // 遍历所有文本框 while (cursor.hasNextToken()) { XmlCursor.TokenType token = cursor.toNextToken(); if (token.isStart()) { XmlObject obj = cursor.getObject(); if (obj instanceof CTR) { CTR ctr = (CTR) obj; if (ctr.getDrawingList().size() > 0) { // 处理绘图对象 for (CTDrawing drawing : ctr.getDrawingList()) { // 查找文本框 XmlCursor drawingCursor = drawing.newCursor(); if (drawingCursor.toChild("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", "anchor") || drawingCursor.toChild("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", "inline")) { // 查找文本框内容 if (drawingCursor.toChild("http://schemas.openxmlformats.org/drawingml/2006/main", "txBox")) { // 获取文本框中的文本 if (drawingCursor.toChild("http://schemas.openxmlformats.org/drawingml/2006/main", "txBody")) { if (drawingCursor.toChild("http://schemas.openxmlformats.org/drawingml/2006/main", "p")) { String text = drawingCursor.getTextValue(); // 替换变量 String replacedText = replaceVariables(text, variables); // 设置回文本框 if (!text.equals(replacedText)) { drawingCursor.setTextValue(replacedText); } } } } } drawingCursor.dispose(); } } } } } cursor.dispose(); } catch (Exception e) { System.err.println("处理文本框时出错: " + e.getMessage()); e.printStackTrace(); } } /** * 处理文档中的所有段落,替换变量 * * @param document Word文档对象 * @param variables 变量映射 */ private static void processParagraphs(XWPFDocument document, Map variables) { for (XWPFParagraph paragraph : document.getParagraphs()) { String text = paragraph.getText(); String replacedText = replaceVariables(text, variables); if (!text.equals(replacedText)) { // 清除原有内容 for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { paragraph.removeRun(i); } // 添加新内容 XWPFRun run = paragraph.createRun(); run.setText(replacedText); } } } /** * 处理文档中的页眉和页脚,替换变量 * * @param document Word文档对象 * @param variables 变量映射 */ private static void processHeadersAndFooters(XWPFDocument document, Map variables) { // 处理页眉 List headers = document.getHeaderList(); for (XWPFHeader header : headers) { for (XWPFParagraph paragraph : header.getParagraphs()) { String text = paragraph.getText(); String replacedText = replaceVariables(text, variables); if (!text.equals(replacedText)) { // 清除原有内容 for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { paragraph.removeRun(i); } // 添加新内容 XWPFRun run = paragraph.createRun(); run.setText(replacedText); } } } // 处理页脚 List footers = document.getFooterList(); for (XWPFFooter footer : footers) { for (XWPFParagraph paragraph : footer.getParagraphs()) { String text = paragraph.getText(); String replacedText = replaceVariables(text, variables); if (!text.equals(replacedText)) { // 清除原有内容 for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) { paragraph.removeRun(i); } // 添加新内容 XWPFRun run = paragraph.createRun(); run.setText(replacedText); } } } } /** * 处理文档中的所有表格,添加行并替换变量 * * @param table Word文档对象 * @param tableData 表格数据 */ private static void processTables(XWPFTable table, List> tableData) { //for (XWPFTable table : document.getTables()) { // 查找模板行(通常第一行是表头,第二行是模板行) if (table.getNumberOfRows() >= 2) { XWPFTableRow templateRow = table.getRow(1); int num = 2; // 为每条数据添加新行 for (Map rowData : tableData) { // 复制模板行 XWPFTableRow newRow = table.insertNewTableRow(2); // 复制每个单元格 for (int i = 0; i < templateRow.getTableCells().size(); i++) { XWPFTableCell templateCell = templateRow.getCell(i); XWPFTableCell newCell = newRow.addNewTableCell(); //清除自带的段落 for (int j = newCell.getParagraphs().size() - 1; j >= 0; j--) { newCell.removeParagraph(j); } // 复制单元格样式 newCell.getCTTc().setTcPr(templateCell.getCTTc().getTcPr()); // 复制段落并替换变量 for (XWPFParagraph paragraph : templateCell.getParagraphs()) { XWPFParagraph newParagraph = newCell.addParagraph(); newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr()); //变量名称 String text = paragraph.getText(); String replacedText = replaceVariables(text, rowData); XWPFRun newRun = newParagraph.createRun(); newRun.setText(replacedText); // 复制运行样式 if (paragraph.getRuns().size() > 0) { XWPFRun templateRun = paragraph.getRuns().get(0); newRun.setBold(templateRun.isBold()); newRun.setItalic(templateRun.isItalic()); String fontFamily = templateRun.getFontFamily(); if (StringUtils.isNotBlank(fontFamily)) { newRun.setFontFamily(fontFamily); } int fontSize = templateRun.getFontSize(); if (fontSize > 0) { newRun.setFontSize(fontSize); } newRun.setColor(templateRun.getColor()); } } } num++; } // 删除模板行 table.removeRow(1); } //} } /** * 处理审批表格表格,添加行并替换变量 * * @param table Word文档对象 * @param tableData 表格数据 */ private static void processApproveTables(XWPFTable table, List> tableData) { // 查找模板行(通常第一行是表头,第二行是模板行) if (table.getNumberOfRows() >= 2) { XWPFTableRow templateRow = table.getRow(1); int num = 2; // 为每条数据添加新行 for (Map rowData : tableData) { // 复制模板行 XWPFTableRow newRow = table.insertNewTableRow(2); // 复制每个单元格 for (int i = 0; i < templateRow.getTableCells().size(); i++) { XWPFTableCell templateCell = templateRow.getCell(i); XWPFTableCell newCell = newRow.addNewTableCell(); //清除自带的段落 for (int j = newCell.getParagraphs().size() - 1; j >= 0; j--) { newCell.removeParagraph(j); } // 复制单元格样式 newCell.getCTTc().setTcPr(templateCell.getCTTc().getTcPr()); // 复制段落并替换变量 for (XWPFParagraph paragraph : templateCell.getParagraphs()) { XWPFParagraph newParagraph = newCell.addParagraph(); newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr()); //变量名称 String text = paragraph.getText(); String replacedText = replaceVariables(text, rowData); XWPFRun newRun = newParagraph.createRun(); newRun.setText(replacedText); // 复制运行样式 if (paragraph.getRuns().size() > 0) { XWPFRun templateRun = paragraph.getRuns().get(0); newRun.setBold(templateRun.isBold()); newRun.setItalic(templateRun.isItalic()); String fontFamily = templateRun.getFontFamily(); if (StringUtils.isNotBlank(fontFamily)) { newRun.setFontFamily(fontFamily); } int fontSize = templateRun.getFontSize(); if (fontSize > 0) { newRun.setFontSize(fontSize); } newRun.setColor(templateRun.getColor()); } } } num++; } // 删除模板行 table.removeRow(1); } } /** * 替换文本中的变量 * * @param text 原始文本 * @param variables 变量映射 * @return 替换后的文本 */ private static String replaceVariables(String text, Map variables) { if (text == null || variables == null) { return text; } String result = text; for (Map.Entry entry : variables.entrySet()) { String pattern = "\\$\\{" + entry.getKey() + "\\}"; String value = entry.getValue() == null ? "" : entry.getValue(); result = result.replaceAll(pattern, value); } return result; } /** * 获取提报方案字段 * * @param ctx 上下文对象 * @param submitSchemeId 提报方案ID * @return 提报方案字段列表 * @throws IOException * @throws SHRWebException */ public static CmpColumnModels getSubmitSchemeFields(Context ctx, String submitSchemeId) throws SHRWebException { //获取提报方案字段 boolean dynamicColmunRequired = true; Map params = Maps.newHashMap(); params.put("datasource", 2); params.put("costTypeId", ""); params.put("dynamicColmunRequired", dynamicColmunRequired); params.put("hrOrgUnitId", ""); BaseSubmitBillEntryGenerator baseSubmitBillEntryGenerator = new BaseSubmitBillEntryGenerator(); return baseSubmitBillEntryGenerator.getEntryColumnModels(ctx, submitSchemeId, params); } }