) map);
+ }
+
+ // 输出word文件
+ doc.save(outputStream);
+ outputStream.flush();
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+
+ /**
+ * cleanDocumentPart
+ *
+ * @param documentPart
+ */
+ public static boolean cleanDocumentPart(MainDocumentPart documentPart) throws Exception {
+ if (documentPart == null) {
+ return false;
+ }
+ Document document = documentPart.getContents();
+ String wmlTemplate =
+ XmlUtils.marshaltoString(document, true, false, Context.jc);
+ document = (Document) XmlUtils.unwrap(DocxVariableClearUtils.doCleanDocumentPart(wmlTemplate, Context.jc));
+ documentPart.setContents(document);
+ return true;
+ }
+
+ /**
+ * 清扫 docx4j 模板变量字符,通常以${variable}形式
+ *
+ * XXX: 主要在上传模板时处理一下, 后续
+ *
+ * @author liliang
+ * @since 2018-11-07
+ */
+ private static class DocxVariableClearUtils {
+
+
+ /**
+ * 去任意XML标签
+ */
+ private static final Pattern XML_PATTERN = Pattern.compile("<[^>]*>");
+
+ private DocxVariableClearUtils() {
+ }
+
+ /**
+ * start符号
+ */
+ private static final char PREFIX = '$';
+
+ /**
+ * 中包含
+ */
+ private static final char LEFT_BRACE = '{';
+
+ /**
+ * 结尾
+ */
+ private static final char RIGHT_BRACE = '}';
+
+ /**
+ * 未开始
+ */
+ private static final int NONE_START = -1;
+
+ /**
+ * 未开始
+ */
+ private static final int NONE_START_INDEX = -1;
+
+ /**
+ * 开始
+ */
+ private static final int PREFIX_STATUS = 1;
+
+ /**
+ * 左括号
+ */
+ private static final int LEFT_BRACE_STATUS = 2;
+
+ /**
+ * 右括号
+ */
+ private static final int RIGHT_BRACE_STATUS = 3;
+
+
+ /**
+ * doCleanDocumentPart
+ *
+ * @param wmlTemplate
+ * @param jc
+ * @return
+ * @throws JAXBException
+ */
+ private static Object doCleanDocumentPart(String wmlTemplate, JAXBContext jc) throws JAXBException {
+ // 进入变量块位置
+ int curStatus = NONE_START;
+ // 开始位置
+ int keyStartIndex = NONE_START_INDEX;
+ // 当前位置
+ int curIndex = 0;
+ char[] textCharacters = wmlTemplate.toCharArray();
+ StringBuilder documentBuilder = new StringBuilder(textCharacters.length);
+ documentBuilder.append(textCharacters);
+ // 新文档
+ StringBuilder newDocumentBuilder = new StringBuilder(textCharacters.length);
+ // 最后一次写位置
+ int lastWriteIndex = 0;
+ for (char c : textCharacters) {
+ switch (c) {
+ case PREFIX:
+ // TODO 不管其何状态直接修改指针,这也意味着变量名称里面不能有PREFIX
+ keyStartIndex = curIndex;
+ curStatus = PREFIX_STATUS;
+ break;
+ case LEFT_BRACE:
+ if (curStatus == PREFIX_STATUS) {
+ curStatus = LEFT_BRACE_STATUS;
+ }
+ break;
+ case RIGHT_BRACE:
+ if (curStatus == LEFT_BRACE_STATUS) {
+ // 接上之前的字符
+ newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex, keyStartIndex));
+ // 结束位置
+ int keyEndIndex = curIndex + 1;
+ // 替换
+ String rawKey = documentBuilder.substring(keyStartIndex, keyEndIndex);
+ // 干掉多余标签
+ String mappingKey = XML_PATTERN.matcher(rawKey).replaceAll("");
+ if (!mappingKey.equals(rawKey)) {
+ char[] rawKeyChars = rawKey.toCharArray();
+ // 保留原格式
+ StringBuilder rawStringBuilder = new StringBuilder(rawKey.length());
+ // 去掉变量引用字符
+ for (char rawChar : rawKeyChars) {
+ if (rawChar == PREFIX || rawChar == LEFT_BRACE || rawChar == RIGHT_BRACE) {
+ continue;
+ }
+ rawStringBuilder.append(rawChar);
+ }
+ // FIXME 要求变量连在一起
+ String variable = mappingKey.substring(2, mappingKey.length() - 1);
+ int variableStart = rawStringBuilder.indexOf(variable);
+ if (variableStart > 0) {
+ rawStringBuilder = rawStringBuilder.replace(variableStart, variableStart + variable.length(), mappingKey);
+ }
+ newDocumentBuilder.append(rawStringBuilder.toString());
+ } else {
+ newDocumentBuilder.append(mappingKey);
+ }
+ lastWriteIndex = keyEndIndex;
+
+ curStatus = NONE_START;
+ keyStartIndex = NONE_START_INDEX;
+ }
+ default:
+ break;
+ }
+ curIndex++;
+ }
+ // 余部
+ if (lastWriteIndex < documentBuilder.length()) {
+ newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex));
+ }
+ return XmlUtils.unmarshalString(newDocumentBuilder.toString(), jc);
+ }
+
+ }
+}
diff --git a/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/project/util/WordUtil.java b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/project/util/WordUtil.java
new file mode 100644
index 000000000..3872677ca
--- /dev/null
+++ b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/project/util/WordUtil.java
@@ -0,0 +1,315 @@
+package com.jero.modules.project.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.IOUtils;
+import org.docx4j.Docx4J;
+import org.docx4j.TraversalUtil;
+import org.docx4j.XmlUtils;
+import org.docx4j.convert.out.FOSettings;
+import org.docx4j.convert.out.HTMLSettings;
+import org.docx4j.dml.wordprocessingDrawing.Inline;
+import org.docx4j.finders.ClassFinder;
+import org.docx4j.finders.RangeFinder;
+import org.docx4j.fonts.IdentityPlusMapper;
+import org.docx4j.fonts.Mapper;
+import org.docx4j.fonts.PhysicalFonts;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
+import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
+import org.docx4j.wml.Body;
+import org.docx4j.wml.CTBookmark;
+import org.docx4j.wml.Document;
+import org.docx4j.wml.Drawing;
+import org.docx4j.wml.ObjectFactory;
+import org.docx4j.wml.P;
+import org.docx4j.wml.R;
+import org.docx4j.wml.Tbl;
+import org.docx4j.wml.Text;
+import org.docx4j.wml.Tr;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.FileCopyUtils;
+
+public class WordUtil {
+ private static final Logger log = LoggerFactory.getLogger(WordUtil.class);
+
+ public WordUtil() {
+ }
+
+ public static void replaceVariable(String templatePath, Map map, String outPath) throws Exception {
+ WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(templatePath));
+ MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
+ Docx4jUtils.cleanDocumentPart(documentPart);
+ documentPart.variableReplace((HashMap) map);
+ Docx4J.save(wordMLPackage, new File(outPath));
+ }
+
+ public static void replaceVariable(InputStream inputStream, Map map, OutputStream outputStream) throws Exception {
+ WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputStream);
+ MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
+ Docx4jUtils.cleanDocumentPart(documentPart);
+ documentPart.variableReplace((HashMap) map);
+ Docx4J.save(wordMLPackage, outputStream);
+ }
+
+ public static void replaceVariable(WordprocessingMLPackage wordMLPackage, Map map) throws Exception {
+ MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
+ Docx4jUtils.cleanDocumentPart(documentPart);
+ documentPart.variableReplace((HashMap) map);
+ }
+
+ public static void replaceTable(String templatePath, int tableNum, int rowNum, List