();
+
+ /**
+ * 添加
+ */
+ public static void add(String mimeType, String extension) {
+ if (!mimeTypeToExtensionMap.containsKey(mimeType)) {
+ mimeTypeToExtensionMap.put(mimeType, extension);
+ }
+ extensionToMimeTypeMap.put(extension, mimeType);
+ }
+
+ /**
+ * 根据extension获取mimeType
+ */
+ public static String getByExtension(String extension) {
+ if (extension == null || extension.isEmpty()) {
+ return null;
+ }
+ return extensionToMimeTypeMap.get(extension);
+ }
+
+ /**
+ * 根据mimeType获取extension
+ */
+ public static String getByMimeType(String mimeType) {
+ if (mimeType == null || mimeType.isEmpty()) {
+ return null;
+ }
+ return mimeTypeToExtensionMap.get(mimeType);
+ }
+
+ /**
+ * mimeType重复的,默认加载第一个
+ */
+ static {
+ add("application/andrew-inset", "ez");
+ add("application/dsptype", "tsp");
+ add("application/json", "json");
+ add("application/futuresplash", "spl");
+ add("application/hta", "hta");
+ add("application/mac-binhex40", "hqx");
+ add("application/mac-compactpro", "cpt");
+ add("application/mathematica", "nb");
+ add("application/msaccess", "mdb");
+ add("application/oda", "oda");
+ add("application/ogg", "ogg");
+ add("application/pdf", "pdf");
+ add("application/pgp-keys", "key");
+ add("application/pgp-signature", "pgp");
+ add("application/pics-rules", "prf");
+ add("application/rar", "rar");
+ add("application/rdf+xml", "rdf");
+ add("application/rss+xml", "rss");
+ add("application/zip", "zip");
+ add("application/vnd.android.package-archive", "apk");
+ add("application/vnd.cinderella", "cdy");
+ add("application/vnd.ms-pki.stl", "stl");
+ add("application/vnd.oasis.opendocument.database", "odb");
+ add("application/vnd.oasis.opendocument.formula", "odf");
+ add("application/vnd.oasis.opendocument.graphics", "odg");
+ add("application/vnd.oasis.opendocument.graphics-template", "otg");
+ add("application/vnd.oasis.opendocument.image", "odi");
+ add("application/vnd.oasis.opendocument.spreadsheet", "ods");
+ add("application/vnd.oasis.opendocument.spreadsheet-template", "ots");
+ add("application/vnd.oasis.opendocument.text", "odt");
+ add("application/vnd.oasis.opendocument.text-master", "odm");
+ add("application/vnd.oasis.opendocument.text-template", "ott");
+ add("application/vnd.oasis.opendocument.text-web", "oth");
+ add("application/vnd.google-earth.kml+xml", "kml");
+ add("application/vnd.google-earth.kmz", "kmz");
+ add("application/msword", "doc");
+ add("application/msword", "dot");
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx");
+ add("application/vnd.ms-excel", "xls");
+ add("application/vnd.ms-excel", "xlt");
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx");
+ add("application/vnd.ms-powerpoint", "ppt");
+ add("application/vnd.ms-powerpoint", "pot");
+ add("application/vnd.ms-powerpoint", "pps");
+ add("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
+ add("application/vnd.openxmlformats-officedocument.presentationml.template", "potx");
+ add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx");
+ add("application/vnd.rim.cod", "cod");
+ add("application/vnd.smaf", "mmf");
+ add("application/vnd.stardivision.calc", "sdc");
+ add("application/vnd.stardivision.draw", "sda");
+ add("application/vnd.stardivision.impress", "sdd");
+ add("application/vnd.stardivision.impress", "sdp");
+ add("application/vnd.stardivision.math", "smf");
+ add("application/vnd.stardivision.writer", "sdw");
+ add("application/vnd.stardivision.writer", "vor");
+ add("application/vnd.stardivision.writer-global", "sgl");
+ add("application/vnd.sun.xml.calc", "sxc");
+ add("application/vnd.sun.xml.calc.template", "stc");
+ add("application/vnd.sun.xml.draw", "sxd");
+ add("application/vnd.sun.xml.draw.template", "std");
+ add("application/vnd.sun.xml.impress", "sxi");
+ add("application/vnd.sun.xml.impress.template", "sti");
+ add("application/vnd.sun.xml.math", "sxm");
+ add("application/vnd.sun.xml.writer", "sxw");
+ add("application/vnd.sun.xml.writer.global", "sxg");
+ add("application/vnd.sun.xml.writer.template", "stw");
+ add("application/vnd.visio", "vsd");
+ add("application/x-abiword", "abw");
+ add("application/x-apple-diskimage", "dmg");
+ add("application/x-bcpio", "bcpio");
+ add("application/x-bittorrent", "torrent");
+ add("application/x-cdf", "cdf");
+ add("application/x-cdlink", "vcd");
+ add("application/x-chess-pgn", "pgn");
+ add("application/x-cpio", "cpio");
+ add("application/x-debian-package", "deb");
+ add("application/x-debian-package", "udeb");
+ add("application/x-director", "dcr");
+ add("application/x-director", "dir");
+ add("application/x-director", "dxr");
+ add("application/x-dms", "dms");
+ add("application/x-doom", "wad");
+ add("application/x-dvi", "dvi");
+ add("application/x-flac", "flac");
+ add("application/x-font", "pfa");
+ add("application/x-font", "pfb");
+ add("application/x-font", "gsf");
+ add("application/x-font", "pcf");
+ add("application/x-font", "pcf.Z");
+ add("application/x-freemind", "mm");
+ add("application/x-futuresplash", "spl");
+ add("application/x-gnumeric", "gnumeric");
+ add("application/x-go-sgf", "sgf");
+ add("application/x-graphing-calculator", "gcf");
+ add("application/x-gtar", "gtar");
+ add("application/x-gtar", "tgz");
+ add("application/x-gtar", "taz");
+ add("application/x-hdf", "hdf");
+ add("application/x-ica", "ica");
+ add("application/x-internet-signup", "ins");
+ add("application/x-internet-signup", "isp");
+ add("application/x-iphone", "iii");
+ add("application/x-iso9660-image", "iso");
+ add("application/x-jmol", "jmz");
+ add("application/x-kchart", "chrt");
+ add("application/x-killustrator", "kil");
+ add("application/x-koan", "skp");
+ add("application/x-koan", "skd");
+ add("application/x-koan", "skt");
+ add("application/x-koan", "skm");
+ add("application/x-kpresenter", "kpr");
+ add("application/x-kpresenter", "kpt");
+ add("application/x-kspread", "ksp");
+ add("application/x-kword", "kwd");
+ add("application/x-kword", "kwt");
+ add("application/x-latex", "latex");
+ add("application/x-lha", "lha");
+ add("application/x-lzh", "lzh");
+ add("application/x-lzx", "lzx");
+ add("application/x-maker", "frm");
+ add("application/x-maker", "maker");
+ add("application/x-maker", "frame");
+ add("application/x-maker", "fb");
+ add("application/x-maker", "book");
+ add("application/x-maker", "fbdoc");
+ add("application/x-mif", "mif");
+ add("application/x-ms-wmd", "wmd");
+ add("application/x-ms-wmz", "wmz");
+ add("application/x-msi", "msi");
+ add("application/x-ns-proxy-autoconfig", "pac");
+ add("application/x-nwc", "nwc");
+ add("application/x-object", "o");
+ add("application/x-oz-application", "oza");
+ add("application/x-pkcs12", "p12");
+ add("application/x-pkcs12", "pfx");
+ add("application/x-pkcs7-certreqresp", "p7r");
+ add("application/x-pkcs7-crl", "crl");
+ add("application/x-quicktimeplayer", "qtl");
+ add("application/x-shar", "shar");
+ add("application/x-shockwave-flash", "swf");
+ add("application/x-stuffit", "sit");
+ add("application/x-sv4cpio", "sv4cpio");
+ add("application/x-sv4crc", "sv4crc");
+ add("application/x-tar", "tar");
+ add("application/x-texinfo", "texinfo");
+ add("application/x-texinfo", "texi");
+ add("application/x-troff", "t");
+ add("application/x-troff", "roff");
+ add("application/x-troff-man", "man");
+ add("application/x-ustar", "ustar");
+ add("application/x-wais-source", "src");
+ add("application/x-wingz", "wz");
+ add("application/x-webarchive", "webarchive");
+ add("application/x-webarchive-xml", "webarchivexml");
+ add("application/x-x509-ca-cert", "crt");
+ add("application/x-x509-user-cert", "crt");
+ add("application/x-xcf", "xcf");
+ add("application/x-xfig", "fig");
+ add("application/xhtml+xml", "xhtml");
+ add("audio/3gpp", "3gpp");
+ add("audio/amr", "amr");
+ add("audio/basic", "snd");
+ add("audio/midi", "mid");
+ add("audio/midi", "midi");
+ add("audio/midi", "kar");
+ add("audio/midi", "xmf");
+ add("audio/mobile-xmf", "mxmf");
+ add("audio/mpeg", "mpga");
+ add("audio/mpeg", "mpega");
+ add("audio/mpeg", "mp2");
+ add("audio/mpeg", "mp3");
+ add("audio/mpeg", "m4a");
+ add("audio/mpegurl", "m3u");
+ add("audio/prs.sid", "sid");
+ add("audio/x-aiff", "aif");
+ add("audio/x-aiff", "aiff");
+ add("audio/x-aiff", "aifc");
+ add("audio/x-gsm", "gsm");
+ add("audio/x-mpegurl", "m3u");
+ add("audio/x-ms-wma", "wma");
+ add("audio/x-ms-wax", "wax");
+ add("audio/x-pn-realaudio", "ra");
+ add("audio/x-pn-realaudio", "rm");
+ add("audio/x-pn-realaudio", "ram");
+ add("audio/x-realaudio", "ra");
+ add("audio/x-scpls", "pls");
+ add("audio/x-sd2", "sd2");
+ add("audio/x-wav", "wav");
+ add("image/bmp", "bmp");
+ add("image/gif", "gif");
+ add("image/ico", "cur");
+ add("image/ico", "ico");
+ add("image/ief", "ief");
+ add("image/jpeg", "jpeg");
+ add("image/jpeg", "jpg");
+ add("image/jpeg", "jpe");
+ add("image/pcx", "pcx");
+ add("image/png", "png");
+ add("image/svg+xml", "svg");
+ add("image/svg+xml", "svgz");
+ add("image/tiff", "tiff");
+ add("image/tiff", "tif");
+ add("image/vnd.djvu", "djvu");
+ add("image/vnd.djvu", "djv");
+ add("image/vnd.wap.wbmp", "wbmp");
+ add("image/x-cmu-raster", "ras");
+ add("image/x-coreldraw", "cdr");
+ add("image/x-coreldrawpattern", "pat");
+ add("image/x-coreldrawtemplate", "cdt");
+ add("image/x-corelphotopaint", "cpt");
+ add("image/x-icon", "ico");
+ add("image/x-jg", "art");
+ add("image/x-jng", "jng");
+ add("image/x-ms-bmp", "bmp");
+ add("image/x-photoshop", "psd");
+ add("image/x-portable-anymap", "pnm");
+ add("image/x-portable-bitmap", "pbm");
+ add("image/x-portable-graymap", "pgm");
+ add("image/x-portable-pixmap", "ppm");
+ add("image/x-rgb", "rgb");
+ add("image/x-xbitmap", "xbm");
+ add("image/x-xpixmap", "xpm");
+ add("image/x-xwindowdump", "xwd");
+ add("model/iges", "igs");
+ add("model/iges", "iges");
+ add("model/mesh", "msh");
+ add("model/mesh", "mesh");
+ add("model/mesh", "silo");
+ add("text/calendar", "ics");
+ add("text/calendar", "icz");
+ add("text/comma-separated-values", "csv");
+ add("text/css", "css");
+ add("text/html", "htm");
+ add("text/html", "html");
+ add("text/h323", "323");
+ add("text/iuls", "uls");
+ add("text/mathml", "mml");
+ add("text/plain", "txt");
+ add("text/plain", "asc");
+ add("text/plain", "text");
+ add("text/plain", "diff");
+ add("text/plain", "po");
+ add("text/richtext", "rtx");
+ add("text/rtf", "rtf");
+ add("text/texmacs", "ts");
+ add("text/text", "phps");
+ add("text/tab-separated-values", "tsv");
+ add("text/xml", "xml");
+ add("text/x-bibtex", "bib");
+ add("text/x-boo", "boo");
+ add("text/x-c++hdr", "h++");
+ add("text/x-c++hdr", "hpp");
+ add("text/x-c++hdr", "hxx");
+ add("text/x-c++hdr", "hh");
+ add("text/x-c++src", "c++");
+ add("text/x-c++src", "cpp");
+ add("text/x-c++src", "cxx");
+ add("text/x-chdr", "h");
+ add("text/x-component", "htc");
+ add("text/x-csh", "csh");
+ add("text/x-csrc", "c");
+ add("text/x-dsrc", "d");
+ add("text/x-haskell", "hs");
+ add("text/x-java", "java");
+ add("text/x-literate-haskell", "lhs");
+ add("text/x-moc", "moc");
+ add("text/x-pascal", "p");
+ add("text/x-pascal", "pas");
+ add("text/x-pcs-gcd", "gcd");
+ add("text/x-setext", "etx");
+ add("text/x-tcl", "tcl");
+ add("text/x-tex", "tex");
+ add("text/x-tex", "ltx");
+ add("text/x-tex", "sty");
+ add("text/x-tex", "cls");
+ add("text/x-vcalendar", "vcs");
+ add("text/x-vcard", "vcf");
+ add("video/3gpp", "3gpp");
+ add("video/3gpp", "3gp");
+ add("video/3gpp", "3g2");
+ add("video/dl", "dl");
+ add("video/dv", "dif");
+ add("video/dv", "dv");
+ add("video/fli", "fli");
+ add("video/m4v", "m4v");
+ add("video/mpeg", "mpeg");
+ add("video/mpeg", "mpg");
+ add("video/mpeg", "mpe");
+ add("video/mp4", "mp4");
+ add("video/mpeg", "VOB");
+ add("video/quicktime", "qt");
+ add("video/quicktime", "mov");
+ add("video/vnd.mpegurl", "mxu");
+ add("video/x-la-asf", "lsf");
+ add("video/x-la-asf", "lsx");
+ add("video/x-mng", "mng");
+ add("video/x-ms-asf", "asf");
+ add("video/x-ms-asf", "asx");
+ add("video/x-ms-wm", "wm");
+ add("video/x-ms-wmv", "wmv");
+ add("video/x-ms-wmx", "wmx");
+ add("video/x-ms-wvx", "wvx");
+ add("video/x-msvideo", "avi");
+ add("video/x-sgi-movie", "movie");
+ add("x-conference/x-cooltalk", "ice");
+ add("x-epoc/x-sisx-app", "sisx");
+ }
+}
diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java
new file mode 100644
index 00000000..c55e5b35
--- /dev/null
+++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordImageLabel.java
@@ -0,0 +1,115 @@
+/**********************************************************************
+* $Id: WordImageLabel.java WordImageLabel ,v0.1 2018年9月19日 下午8:50:48 byx Exp $
+* Copyright ©2018 sida . All rights reserved
+***********************************************************************/
+
+package org.jeecg.modules.tools.word;
+
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+* 功能说明:从标签中读取键值,宽高和后缀内容
+* 创建者:byx
+* 创建时间:2018年9月19日
+*
+* 修改时间: 修改者:
+* 修改内容:
+*
+*/
+public class WordImageLabel {
+
+ public static final String SPLIT_STR = ",";
+
+ private String key = ""; //键值
+
+ private int width = 0; //宽
+
+ private int height = 0; //高
+
+ private String picAttch = ""; //后缀值
+
+ public WordImageLabel(Object obj){
+ if(obj instanceof String){
+ this.wordImageLabel((String)obj);
+ }
+ }
+
+ /**
+ *
+ * 功能说明 : 如:‘image,10,10,后缀’
+ * 创建者 : byx
+ * 修改日期 : 2018年9月19日
+ * @param value
+ */
+ public WordImageLabel(String value){
+ super();
+ this.wordImageLabel(value);
+ }
+
+ /**
+ *
+ * 功能说明 : 如:‘image,10,10,后缀’
+ * 创建者 : byx
+ * 修改日期 : 2018年9月19日
+ * @param value
+ */
+ public void wordImageLabel(String value) {
+ if(StringUtils.isNotBlank(value)){
+ String[] strList = StringUtils.split(value, SPLIT_STR);
+ if(strList.length == 0){
+ key = value;
+ }else if(strList.length == 1){
+ key = strList[0];
+ }else if(strList.length == 2){
+ key = strList[0];
+ width = Integer.valueOf(strList[1]);
+ }else if(strList.length == 3){
+ key = strList[0];
+ width = Integer.valueOf(strList[1]);
+ height = Integer.valueOf(strList[2]);
+ }else if(strList.length == 4){
+ key = strList[0];
+ width = Integer.valueOf(strList[1]);
+ height = Integer.valueOf(strList[2]);
+ picAttch = strList[3];
+ }
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public String getPicAttch() {
+ return picAttch;
+ }
+
+ public void setPicAttch(String picAttch) {
+ this.picAttch = picAttch;
+ }
+
+
+
+
+}
diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java
new file mode 100644
index 00000000..c51a74f3
--- /dev/null
+++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordOperator.java
@@ -0,0 +1,1154 @@
+package org.jeecg.modules.tools.word;
+
+import cn.hutool.core.io.IoUtil;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.util.Units;
+import org.apache.poi.xwpf.usermodel.*;
+import org.jeecg.common.util.SpringContextHolder;
+import org.jeecg.modules.tools.CommonUtil;
+import org.jeecg.modules.tools.FileUtils;
+import org.jeecg.modules.tools.Global;
+import org.jeecg.modules.tools.IdGen;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 对word模板的操作,包含替换文本和列表向下扩展两种
+ * 本工具类的使用需要配合ExportWord.class,具体Demo看ExportWord类即可
+ * @author byx
+ */
+public class WordOperator {
+
+ private static Logger logger = LoggerFactory.getLogger(WordOperator.class);
+
+ /**
+ * 模板替换标签开头
+ */
+ private final static String LABEL_STR = "{";
+ /**
+ * 模板替换标签结尾
+ */
+ private final static String LABEL_END = "}";
+ /**
+ * 模板替换图片标签开头(List)
+ */
+ private final static String LABEL_IMAGE_STR = "<";
+ /**
+ * 模板替换图片标签结尾(List)
+ */
+ private final static String LABEL_IMAGE_END = ">";
+
+ /**
+ * 模板替换图片标签开头(List)
+ */
+ private final static String LABEL_RICH_TEXT_STR = "
+ * 因为微软只提供XWPFRun对象替换文本,而它在切割段落文本的时候却是非常没有规律的,故而本方法要求模板必须遵循一定的规范来编写:
+ * 1.模板里除了标签外,其余地方禁止使用"{"和"}"字符串;
+ * 2.不允许出现"{}"这种写法,即标签必须包含key;
+ */
+ public WordOperator(InputStream is) throws Exception {
+ doc = new XWPFDocument(is);
+ }
+
+
+
+ /**
+ * 要替换标签的KV对
+ */
+ private Map replaceMap;
+
+ public Map getReplaceMap() {
+ return replaceMap;
+ }
+
+ public void setReplaceMap(Map replaceMap) {
+ this.replaceMap = replaceMap;
+ }
+
+ /**
+ *
+ * 功能说明 : 创建图片,根据段落
+ * 创建者 : byx
+ * 修改日期 : 2018年9月15日
+ * @param pic 图片位置
+ * @param picPath 图片路径
+ * @param width 图片宽度
+ * @param height 图片高度
+ * @param picAttch 图片后缀文字
+ */
+ public void createPicture(XWPFParagraph pic, String picPath, int width, int height, String picAttch){
+ pic.setAlignment(ParagraphAlignment.CENTER);
+
+ XWPFRun run = pic.createRun();
+
+ try {
+ if(StringUtils.isBlank(picPath) || picPath.lastIndexOf(".") == -1){
+ logger.error("文件路径错误:");
+ logger.error(picPath);
+ return;
+ }
+ //根据文件后缀名设置图片格式
+ String picType = picPath.substring(picPath.lastIndexOf("\\.")+1);
+ int res = XWPFDocument.PICTURE_TYPE_PICT;
+ if(picType != null){
+ if(picType.equalsIgnoreCase("png")){
+ res = XWPFDocument.PICTURE_TYPE_PNG;
+ }else if(picType.equalsIgnoreCase("gif")) {
+ res = XWPFDocument.PICTURE_TYPE_GIF;
+ }else if(picType.equalsIgnoreCase("emf")){
+ res = XWPFDocument.PICTURE_TYPE_EMF;
+ }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
+ res = XWPFDocument.PICTURE_TYPE_JPEG;
+ }else if(picType.equalsIgnoreCase("wmf")){
+ res = XWPFDocument.PICTURE_TYPE_WMF;
+ }else if(picType.equalsIgnoreCase("pict")){
+ res = XWPFDocument.PICTURE_TYPE_PICT;
+ }else if(picType.equalsIgnoreCase("dib")){
+ res = XWPFDocument.PICTURE_TYPE_DIB;
+ }else if(picType.equalsIgnoreCase("tiff")){
+ res = XWPFDocument.PICTURE_TYPE_TIFF;
+ }else if(picType.equalsIgnoreCase("eps")){
+ res = XWPFDocument.PICTURE_TYPE_EPS;
+ }else if(picType.equalsIgnoreCase("bmp")){
+ res = XWPFDocument.PICTURE_TYPE_BMP;
+ }else if(picType.equalsIgnoreCase("wpg")){
+ res = XWPFDocument.PICTURE_TYPE_WPG;
+ }
+ }
+ //塞进文档中
+// doc.addPictureData(new FileInputStream(picPath),res);
+ if(StringUtils.isNotBlank(picAttch)){
+ run.setText(picAttch);
+ }
+ //补充完整路径
+ Global global = SpringContextHolder.getBean(Global.class);
+ InputStream is = new FileInputStream(global.uploadPath + "/" + picPath);
+// run.addPicture(is,res,picPath,Units.pixelToEMU(width), Units.pixelToEMU(height));
+ run.addPicture(is,res,picPath,Units.toEMU(width), Units.toEMU(height));
+// run.addPicture(is,res,picPath,width, height);
+ IoUtil.close(is);
+ } catch (InvalidFormatException e) {
+ logger.error(e.toString());
+ e.printStackTrace();
+ } catch (IOException e) {
+ logger.error("没有找到相应的文件!");
+ logger.error(e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ *
+ * 功能说明 : 多个图片支持(|分隔)
+ * 创建者 : byx
+ * 修改日期 : 2018年9月26日
+ * @param pic 图片位置
+ * @param picPath 图片路径
+ * @param width 图片宽度
+ * @param height 图片高度
+ * @param picAttch 图片后缀文字
+ */
+ public void createPicturePlus(XWPFParagraph pic, String picPath, int width, int height, String picAttch){
+ if(StringUtils.isBlank(picPath)){
+ logger.warn("文件路径错误:");
+ logger.warn(picPath);
+ return;
+ }
+ if(StringUtils.indexOf(picPath, ",") == -1){
+ //为单个时直接插入图片
+ createPicture(pic,picPath,width,height,picAttch);
+ }else{
+ //多个时,
+ String[] paths = StringUtils.split(picPath,",");
+ for (String s : paths) {
+ createPicture(pic,s,width,height,picAttch);
+ }
+ }
+// createPicture
+ }
+
+ /**
+ * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换
+ */
+ public WordOperator replaceText(Map replaceMap) {
+ this.replaceMap = replaceMap;
+ // 替换文本内容的标签
+ List paragraphList = doc.getParagraphs();
+ replaceText(paragraphList);
+ // 替换表格内部标签
+ List tableList = doc.getTables();
+ for (XWPFTable table : tableList) {
+ for (int i = 0; i < table.getNumberOfRows(); i++) {
+ XWPFTableRow row = table.getRow(i);
+ List tableCellList = row.getTableCells();
+ for (XWPFTableCell cell : tableCellList) {
+ replaceText(cell.getParagraphs());
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换(带有替换图片的功能)
+ */
+ public WordOperator replaceTextPlus(Map replaceMap) {
+ this.replaceMap = replaceMap;
+ // 替换文本内容的标签
+ List paragraphList = doc.getParagraphs();
+ replaceTextPlus(paragraphList);
+ // 替换表格内部标签
+ List tableList = doc.getTables();
+ for (XWPFTable table : tableList) {
+ for (int i = 0; i < table.getNumberOfRows(); i++) {
+ XWPFTableRow row = table.getRow(i);
+ List tableCellList = row.getTableCells();
+ for (XWPFTableCell cell : tableCellList) {
+ replaceTextPlus(cell.getParagraphs());
+ }
+ }
+ }
+ return this;
+ }
+
+
+ public WordOperator replaceTextPlusRemove(Map replaceMap,String removeList) {
+ this.replaceMap = replaceMap;
+ // 替换文本内容的标签
+ List paragraphList = doc.getParagraphs();
+ replaceTextPlus(paragraphList);
+ // 替换表格内部标签
+ List tableList = doc.getTables();
+ for (XWPFTable table : tableList) {
+ for (int i = 0; i < table.getNumberOfRows(); i++) {
+ XWPFTableRow row = table.getRow(i);
+ List tableCellList = row.getTableCells();
+ for (XWPFTableCell cell : tableCellList) {
+ replaceTextPlus(cell.getParagraphs());
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 在word模板文档里,用{key}标签来表示要替换的内容,把key对应的value存储于replaceMap里,即可完成替换(带图片,仅支持一个图片)
+ */
+ public WordOperator replaceTextAndCreatePicture(Map replaceMap) {
+ this.replaceMap = replaceMap;
+ // 替换文本内容的标签
+ List paragraphList = doc.getParagraphs();
+ replaceText(paragraphList);
+ // 替换表格内部标签
+ List tableList = doc.getTables();
+ for (XWPFTable table : tableList) {
+ for (int i = 0; i < table.getNumberOfRows(); i++) {
+ XWPFTableRow row = table.getRow(i);
+ List tableCellList = row.getTableCells();
+ for (XWPFTableCell cell : tableCellList) {
+ replaceTextAndCreatePicture(cell.getParagraphs());
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用
+ */
+ private void replaceText(List paragraphList) {
+ // 取出word模板里的全部段落,遍历
+ for (int i = 0; i < paragraphList.size(); i++) {
+ XWPFParagraph paragraph = paragraphList.get(i);
+ // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑
+ String paragraphText = paragraph.getText();
+ if (!StringUtils.isBlank(paragraphText) && paragraphText.indexOf(LABEL_STR) != -1 && paragraphText.indexOf(LABEL_END) != -1) {
+ // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能
+ List runList = paragraph.getRuns();
+ // 组装replaceMap的key
+ String key = "";
+ // 是否检测到有"{"字符串,有为true,没有为false
+ boolean include = false;
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ String text = run.getText(0);
+ // 取出每一个小段里标签头和尾的角标
+ int labelStrIndex = StringUtils.indexOf(text, LABEL_STR);
+ int labelEndIndex = StringUtils.indexOf(text, LABEL_END);
+ // 只有{
+ if (labelStrIndex != -1 && labelEndIndex == -1) {
+ // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里
+ String textBefore = text.substring(0, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ run.setText(textBefore, 0);
+ key += textAfter;
+ include = true;
+ // 只有}
+ } else if (labelStrIndex == -1 && labelEndIndex != -1) {
+ // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里
+ String textBefore = text.substring(0, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ key += textBefore;
+ Object value = replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ key = "";
+ run.setText(value + textAfter, 0);
+ include = false;
+ // 两个都没有
+ } else if (labelStrIndex == -1 && labelEndIndex == -1) {
+ // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管
+ if (include) {
+ key += text;
+ run.setText("", 0);
+ }
+ // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已
+ } else {
+ // {在前,}在后
+ if (labelStrIndex < labelEndIndex) {
+ // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值)
+ String textBefore = text.substring(0, labelStrIndex);
+ String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ Object value = replaceMap.get(textMiddle);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ run.setText(textBefore + value + textAfter, 0);
+ key = "";
+ include = false;
+ // }在前,{在后
+ } else {
+ // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去
+ String textBefore = text.substring(0, labelEndIndex);
+ String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ key += textBefore;
+ Object value = replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ run.setText(value + textMiddle, 0);
+ key = textAfter;
+ include = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ /**
+ * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用(可以替换为图片)
+ */
+ private void replaceTextPlus(List paragraphList) {
+ //InputStream imageFileIs = null;
+ // 取出word模板里的全部段落,遍历
+ for (int i = 0; i < paragraphList.size(); i++) {
+ XWPFParagraph paragraph = paragraphList.get(i);
+ // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑
+ String paragraphText = paragraph.getText();
+ if (!StringUtils.isBlank(paragraphText) && StringUtils.indexOf(paragraphText,LABEL_STR) != -1 && StringUtils.indexOf(paragraphText,LABEL_END) != -1) {
+ // 每一个段落分很多小段文本,官方API只能通过XWPFRun对象执行替换文本功能
+ List runList = paragraph.getRuns();
+ // 组装replaceMap的key
+ String key = "";
+ // 是否检测到有"{"字符串,有为true,没有为false
+ boolean include = false;
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ //颜色
+// run.setColor("F3C917");
+ //加粗
+// run.setBold(true);
+ //背景色
+// run.getCTR().addNewRPr().addNewHighlight().setVal(STHighlightColor.YELLOW);
+ //底纹
+// CTShd shd = run.getCTR().addNewRPr().addNewShd();
+// shd.setFill("FFFF00");
+
+ String text = run.getText(0);
+ // 取出每一个小段里标签头和尾的角标
+ //是否存在两个{
+ boolean isParagraphReplaceAllText = false;
+ if(StringUtils.ordinalIndexOf(text,LABEL_STR,2) != -1){
+ isParagraphReplaceAllText = true;
+ text = paragraphReplaceAllText(text,replaceMap);
+ }
+ int labelStrIndex = StringUtils.indexOf(text, LABEL_STR);
+ int labelEndIndex = StringUtils.indexOf(text, LABEL_END);
+ // 只有{
+ if (labelStrIndex != -1 && labelEndIndex == -1) {
+ // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里
+ String textBefore = text.substring(0, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ run.setText(textBefore, 0);
+ key += textAfter;
+ include = true;
+ // 只有}
+ } else if (labelStrIndex == -1 && labelEndIndex != -1) {
+ // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里
+ String textBefore = text.substring(0, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ key += textBefore;
+ //Object value = replaceMap.get(key);
+ String value = (String)replaceMap.get(key);
+ if (StringUtils.isBlank(value)) value = "";
+ WordTextStyle style = (WordTextStyle)replaceMap.get(key + "_style");
+ //添加样式
+ if(style != null) style.run(run);
+ if (StringUtils.isBlank(value)) value = "";
+ value = StringEscapeUtils.unescapeHtml4(value);
+ key = "";
+ //--2020-04修改
+ String[] values = value.split("\r\n");
+ if(values.length > 1) {
+ run.setText(values[0],0);
+ for (int x = 1; x < values.length; x++) {
+ //存在分段则新建一个run
+ XWPFRun newrun = paragraph.createRun();
+ //copy样式
+ newrun.getCTR().setRPr(run.getCTR().getRPr());
+ //换行
+ newrun.addBreak();
+ //缩进
+ //newrun.addTab();
+ if(x == values.length - 1) {
+ newrun.setText(values[x] + textAfter);
+ }else {
+ newrun.setText(values[x]);
+ }
+ }
+ }else {
+ run.setText(value + textAfter, 0);
+ }
+ include = false;
+ // 两个都没有
+ } else if (labelStrIndex == -1 && labelEndIndex == -1) {
+ // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管
+ if (include) {
+ key += text;
+ run.setText("", 0);
+ }
+ if(isParagraphReplaceAllText){
+ //替换文本
+ run.setText(text,0);
+ }
+ // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已
+ } else {
+ // {在前,}在后
+ if (labelStrIndex < labelEndIndex) {
+ // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值)
+ String textBefore = text.substring(0, labelStrIndex);
+ String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ String value = (String)replaceMap.get(textMiddle);
+ if (StringUtils.isBlank(value)) value = "";
+ WordTextStyle style = (WordTextStyle)replaceMap.get(textMiddle + "_style");
+ //添加样式
+ if(style != null) style.run(run);
+ value = StringEscapeUtils.unescapeHtml4(value);
+ //--2020-04修改
+ String[] values = value.split("\r\n");
+ if(values.length > 1) {
+ run.setText(textBefore + values[0],0);
+ for (int x = 1; x < values.length; x++) {
+ //存在分段则新建一个run
+ XWPFRun newrun = paragraph.createRun();
+ //copy样式
+ newrun.getCTR().setRPr(run.getCTR().getRPr());
+ //换行
+ newrun.addBreak();
+ //缩进
+ //newrun.addTab();
+ if(x == values.length - 1) {
+ newrun.setText(values[x] + textAfter,0);
+ }else {
+ newrun.setText(values[x]);
+ }
+ }
+ }else {
+ run.setText(textBefore + value + textAfter, 0);
+ }
+
+ key = "";
+ include = false;
+ // }在前,{在后
+ } else {
+ // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去
+ String textBefore = text.substring(0, labelEndIndex);
+ String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ key += textBefore;
+ String value = (String)replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) value = "";
+ WordTextStyle style = (WordTextStyle)replaceMap.get(textMiddle + "_style");
+ //添加样式
+ if(style != null) style.run(run);
+ value = StringEscapeUtils.unescapeHtml4(value);
+ //--2020-04修改
+ String[] values = value.toString().split("\r\n");
+ if(values.length > 1) {
+ run.setText(values[0],0);
+ for (int x = 1; x < values.length; x++) {
+ //存在分段则新建一个run
+ XWPFRun newrun = paragraph.createRun();
+ //copy样式
+ newrun.getCTR().setRPr(run.getCTR().getRPr());
+ //换行
+ newrun.addBreak();
+ //缩进
+ //newrun.addTab();
+ if(x == values.length - 1) {
+ newrun.setText(values[x] + textMiddle,0);
+ }else {
+ newrun.setText(values[x]);
+ }
+ }
+ }else {
+ run.setText(value + textMiddle, 0);
+ }
+ key = textAfter;
+ include = true;
+ }
+ }
+ }
+ //富文本标签(转成图片的富文本)
+ } else if(StringUtils.isNotBlank(paragraphText) && paragraphText.indexOf(LABEL_RICH_TEXT_STR) != -1 && paragraphText.indexOf(LABEL_RICH_TEXT_END) != -1){
+ System.out.println("paragraphText="+paragraphText);
+ //如果匹配到‘<’和‘>’则开始替换为图片
+ // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能
+ List runList = paragraph.getRuns();
+ //组装replaceMap的key
+ String key = "";
+
+ //
+ //摘出key
+ int $labelStrIndex = StringUtils.indexOf(paragraphText, LABEL_RICH_TEXT_STR);
+ int $labelEndIndex = StringUtils.indexOf(paragraphText, LABEL_RICH_TEXT_END);
+
+ //必须存在开始和结束标签
+ if($labelStrIndex != -1 && $labelEndIndex != -1){
+ key = paragraphText;
+ key = key.substring($labelStrIndex + LABEL_RICH_TEXT_STR.length());
+ $labelEndIndex = StringUtils.indexOf(key, LABEL_RICH_TEXT_END);
+ key = key.substring(0,$labelEndIndex);
+ }
+
+ //去除整行(整个paragraph)
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ run.setText("", 0);
+ }
+ //添加图片
+ //创建一个run
+ //XWPFRun $run = paragraph.createRun();
+ Object value = replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ String imagePath = getHtmlImage(value.toString());
+ //try {
+ //imageFileIs = new FileInputStream(new File(imagePath));
+ //$run.addPicture(imageFileIs, 6, "image", 595,842);
+ //} catch (InvalidFormatException | IOException e) {
+ //logger.error("图片读取或添加错误!");
+ //logger.error(e.getMessage(),e);
+ //e.printStackTrace();
+ //}
+ createPicturePlus(paragraph,imagePath,595,842,"");
+ //FileUtils.deleteFile(imagePath);
+ //$run.setText();
+ //图片标签
+ }else if(StringUtils.isNotBlank(paragraphText) && paragraphText.indexOf(LABEL_IMAGE_STR) != -1 && paragraphText.indexOf(LABEL_IMAGE_END) != -1){
+ //如果匹配到‘<’和‘>’则开始替换为图片
+ // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能
+ List runList = paragraph.getRuns();
+ // 组装replaceMap的key
+ String key = "";
+ //读取简易标签对象如:‘image,10,10,后缀’
+ WordImageLabel wordImageLabel = null;
+ // 是否检测到有"<"字符串,有为true,没有为false
+ boolean include = false;
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ String text = run.getText(0);
+ // 取出每一个小段里标签头和尾的角标
+ int labelStrIndex = StringUtils.indexOf(text, LABEL_IMAGE_STR);
+ int labelEndIndex = StringUtils.indexOf(text, LABEL_IMAGE_END);
+ // 只有<
+ if (labelStrIndex != -1 && labelEndIndex == -1) {
+ // 例如有"aaabbb",则aaa>去掉,bbb不动,同时把aaa累加到key里
+ String textBefore = text.substring(0, labelEndIndex);
+// String textAfter = text.substring(labelEndIndex + 1);
+ key += textBefore;
+ //读取简易标签对象
+ wordImageLabel = new WordImageLabel(key);
+ Object value = replaceMap.get(wordImageLabel.getKey());
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ key = "";
+ //创建图片
+ run.setText("", 0);
+// run.setText(value + textAfter, 0);
+ createPicturePlus(paragraph,(String)value,wordImageLabel.getWidth(),wordImageLabel.getHeight(),wordImageLabel.getPicAttch());
+ include = false;
+ // 两个都没有
+ } else if (labelStrIndex == -1 && labelEndIndex == -1) {
+ // 例如有"aaa",如果之前有<了,则把内容累加到key里,同时去掉内容;如果没有<,则说明是普通文本,跳过不管
+ if (include) {
+ key += text;
+ run.setText("", 0);
+ }
+ // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已
+ } else {
+ // <在前,>在后
+ if (labelStrIndex < labelEndIndex) {
+ // 例如有"aaaccc",则aaa不动,去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值)
+// String textBefore = text.substring(0, labelStrIndex);
+ String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex);
+// String textAfter = text.substring(labelEndIndex + 1);
+ wordImageLabel = new WordImageLabel(textMiddle);
+ Object value = replaceMap.get(wordImageLabel.getKey());
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ run.setText("", 0);
+// run.setText(textBefore + value + textAfter, 0);
+ createPicturePlus(paragraph,(String)value,wordImageLabel.getWidth(),wordImageLabel.getHeight(),wordImageLabel.getPicAttch());
+ key = "";
+ include = false;
+ // >在前,<在后
+ } else {
+ // 例如有"aaa>bbb去掉,且进行替换;bbb不动, replaceMap){
+ return CommonUtil.getReplaceMapValue(str,replaceMap,LABEL_STR,LABEL_END);
+ }
+
+ /**
+ * 把替换段落抽取出一个方法,在替换文本和替换表格里都可以调用(带图片),只替换{IMAGE}
+ */
+ private void replaceTextAndCreatePicture(List paragraphList) {
+ // 取出word模板里的全部段落,遍历
+ for (int i = 0; i < paragraphList.size(); i++) {
+ XWPFParagraph paragraph = paragraphList.get(i);
+ // 拿出每一个段落,判断内容里是否包含字符串"{"和"}",只有两者同时存在了才执行替换标签的逻辑
+ String paragraphText = paragraph.getText();
+ if (!StringUtils.isBlank(paragraphText)) {
+ //判断是否为唯一的image关键字
+ if(StringUtils.equals(paragraphText, "{IMAGE}") && paragraphText.indexOf(LABEL_STR) != -1 && paragraphText.indexOf(LABEL_END) != -1){
+ //去掉关键字标记
+ List runList = paragraph.getRuns();
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ run.setText("", 0);
+ }
+ //获取图片地址
+ String picPath = (String) replaceMap.get("IMAGE");
+ //创建图片
+ createPicture(paragraph,picPath,83,83," ");
+ //创建后不需要普通的文字替换了,跳过本段落
+ continue;
+ }
+ // 每一个段落分很多小段文本,官方API只能否通过XWPFRun对象执行替换文本功能
+ List runList = paragraph.getRuns();
+ // 组装replaceMap的key
+ String key = "";
+ // 是否检测到有"{"字符串,有为true,没有为false
+ boolean include = false;
+ for (int j = 0; j < runList.size(); j++) {
+ XWPFRun run = runList.get(j);
+ String text = run.getText(0);
+ // 取出每一个小段里标签头和尾的角标
+ int labelStrIndex = text.indexOf(LABEL_STR);
+ int labelEndIndex = text.indexOf(LABEL_END);
+ // 只有{
+ if (labelStrIndex != -1 && labelEndIndex == -1) {
+ // 例如有"aaa{bbb",则aaa不动,{bbb去掉,同时把bbb累加到key里
+ String textBefore = text.substring(0, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ run.setText(textBefore, 0);
+ //createPicture(paragraph,"");
+ key += textAfter;
+ include = true;
+ // 只有}
+ } else if (labelStrIndex == -1 && labelEndIndex != -1) {
+ // 例如有"aaa}bbb",则aaa}去掉,bbb不动,同时把aaa累加到key里
+ String textBefore = text.substring(0, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ key += textBefore;
+ Object value = replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ key = "";
+ run.setText(value + textAfter, 0);
+ include = false;
+ // 两个都没有
+ } else if (labelStrIndex == -1 && labelEndIndex == -1) {
+ // 例如有"aaa",如果之前有{了,则把内容累加到key里,同时去掉内容;如果没有{,则说明是普通文本,跳过不管
+ if (include) {
+ key += text;
+ run.setText("", 0);
+ }
+ // 两个都存在,这种情况比较复杂。经过多次试验,XWPFRun切割内容,只会同时各出现1次而已
+ } else {
+ // {在前,}在后
+ if (labelStrIndex < labelEndIndex) {
+ // 例如有"aaa{bbb}ccc",则aaa不动,{bbb}去掉同时进行替换,ccc保留(因为有规范,所以这是一个独立完整的标签,且bbb绝对有值)
+ String textBefore = text.substring(0, labelStrIndex);
+ String textMiddle = text.substring(labelStrIndex + 1, labelEndIndex);
+ String textAfter = text.substring(labelEndIndex + 1);
+ Object value = replaceMap.get(textMiddle);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ run.setText(textBefore + value + textAfter, 0);
+ key = "";
+ include = false;
+ // }在前,{在后
+ } else {
+ // 例如有"aaa}bbb{ccc",则aaa}去掉,且进行替换;bbb不动,{ccc去掉,累加到key里去
+ String textBefore = text.substring(0, labelEndIndex);
+ String textMiddle = text.substring(labelEndIndex + 1, labelStrIndex);
+ String textAfter = text.substring(labelStrIndex + 1);
+ key += textBefore;
+ Object value = replaceMap.get(key);
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ value = "";
+ }
+ run.setText(value + textMiddle, 0);
+ key = textAfter;
+ include = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+// public static void main(String[] args) {
+// String text = "";
+// //摘出key
+// int $labelStrIndex = StringUtils.indexOf(text, LABEL_RICH_TEXT_STR);
+// int $labelEndIndex = StringUtils.indexOf(text, LABEL_RICH_TEXT_END);
+//
+//
+// System.out.println("$labelStrIndex=" + $labelStrIndex);
+// System.out.println("$labelEndIndex=" + $labelEndIndex);
+// //必须存在开始和结束标签
+// if($labelStrIndex != -1 && $labelEndIndex != -1){
+// String key = text;
+// key = key.substring($labelStrIndex + LABEL_RICH_TEXT_STR.length());
+// $labelEndIndex = StringUtils.indexOf(key, LABEL_RICH_TEXT_END);
+// System.out.println("key="+key);
+// key = key.substring(0,$labelEndIndex);
+// System.out.println("key="+key);
+//
+// }
+//
+// HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
+//// String htmlstr = "
编号:10001
";
+// String htmlstr = "
编号:10001
";
+//// String htmlstr = "
";
+//// htmlstr = htmlstr.replaceAll("/education/", "http://127.0.0.1:9000/education/");
+// imageGenerator.loadHtml(htmlstr);
+//// imageGenerator.loadUrl("https://www.baidu.com");
+//// imageGenerator.setSize(new Dimension(595,842));
+// try {
+// imageGenerator.getBufferedImage();
+// Thread.sleep(4000);
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+// imageGenerator.saveAsImage("z:/baidu.png");
+// }
+
+ public static void main0(String[] args) {
+ getHtmlImage("aa");
+ }
+ public static String getHtmlImage(String html){
+// String path = Global.getConfig("userfiles.basedir") + "/htmlImageTemp/";
+ String path = "";
+ FileUtils.createDirectory(path);
+ path += IdGen.uuid() + ".png";
+ getHtmlImage(html,path);
+ return path;
+ }
+ public static void getHtmlImage(String html, String path){
+ getHtmlImage(html,8000,path);
+ }
+ public static void getHtmlImage(String html, int sleep, String path){
+// HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
+// imageGenerator.loadHtml(html);
+// imageGenerator.setSize(new Dimension(595,842));
+// try {
+// imageGenerator.getBufferedImage();
+// Thread.sleep(sleep);
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+// imageGenerator.saveAsImage(path);
+ }
+ /**
+ * 表格向下扩展数据行,因为POI对word格式支持较差,故我们做一些约定
+ * 1.约定表格要有一行模板行,用于扩展数据行的样式效仿;
+ * 2.模板表格里最好不要有合并行,如非常需要,则可以用隐藏单元格或设置白色边框的方式巧妙替代合并行;
+ * 3.模板行下方不允许出现其他行
+ * @param tableIndex 表格角标,第1个表为0
+ * @param tmpRowIndex 模板行角标,第1行为0(模板行不是标题行,而是扩展数据行样式的效仿对象)
+ * @param isDelTmpRow true-删除模板行;false-不删除模板行
+ * @param rowlist 要插入的数据集合,格式为[行集合<列集合>,列集合的长度最好与模板行长度一致]
+ */
+ public WordOperator insert2Table(int tableIndex, int tmpRowIndex, boolean isDelTmpRow, List> rowlist) {
+ // 因为需要对rowlist进行添加操作,故而我们重新做一个变量,防止因为改变而产生问题
+ List> dataList = new ArrayList<>(rowlist);
+ // 根据角标取出表格对像
+ XWPFTable table = doc.getTables().get(tableIndex);
+ // 再根据行号获取模板行
+ XWPFTableRow tmpRow = table.getRow(tmpRowIndex);
+ // 不知道是什么原因,最后一列数据会跑到模板行上一行,故而这里在最末添加一组数据,解决这个bug
+ dataList.add(new ArrayList<>());
+ // 遍历数据集合
+ for (int i = 0, len = dataList.size(); i < dataList.size(); i++) {
+ // 计算数据行编号
+ int dataIndex = tmpRowIndex + 1 + i;
+ // 按照模板行样式添加一行到数据行
+ table.addRow(tmpRow, dataIndex);
+ if (i < len - 1) {
+ // 取出这一行
+ XWPFTableRow row = table.getRow(dataIndex);
+ // 取出这一行全部单元格
+ List cellList = row.getTableCells();
+ // 取出这一行对应的数据,数据与单元格角标位置是一致的,一一替换文本
+ List colList = dataList.get(i);
+ for (int j = 0; j < cellList.size(); j++) {
+ setCellText(cellList.get(j), colList, j);
+ }
+ }
+ }
+ // 删掉那一行为了bug而添加的空白行(因为空白行跑到模板行上一行,所以tmpRowIndex为该行角标)
+ table.removeRow(tmpRowIndex);
+ // 删除模版行(因为上面已经删除了空白行了,所以tmpRowIndex就变成了下一行模板行了)
+ if (isDelTmpRow) {
+ table.removeRow(tmpRowIndex);
+ }
+ return this;
+ }
+
+ /**
+ * 替换单元格文本
+ * @param cell 单元格
+ * @param colList 替换文本的集合(角标越位了,就置空)
+ * @param j 单元格角标,也就是文本集合的角标
+ */
+ private void setCellText(XWPFTableCell cell, List colList, int j) {
+// cell.setColor("FF0000");
+ // 无论一个单元格被切割成多少个run,只需要修改第一个即可,改完就return掉了
+ List paragraphs = cell.getParagraphs();
+ for (XWPFParagraph p : paragraphs) {
+ List runs = p.getRuns();
+ for (XWPFRun r : runs) {
+ try {
+ r.setText(colList.get(j), 0);
+ //染上背景色
+// r.getCTR().addNewRPr().addNewHighlight().setVal(STHighlightColor.YELLOW);
+ } catch (Exception e) {
+ r.setText("", 0);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * 填充颜色
+ * @param tableIndex 第几个表格,第一个表格为0
+ * @param rowIndex 表格中的第几行,从0开始算起
+ * @param colIndex 行中的第几单元格,从0开始算起
+ * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB".
+ * @return
+ */
+ public WordOperator fillCollorTable(int tableIndex, int rowIndex, int colIndex, String color) {
+ // 根据角标取出表格对像
+ XWPFTable table = doc.getTables().get(tableIndex);
+ if(table == null){
+ logger.error("没有找到表格!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex);
+ return this;
+ }
+ XWPFTableRow row = table.getRow(rowIndex);
+ if(row == null){
+ logger.error("没有找到row!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex);
+ return this;
+ }
+ XWPFTableCell col = row.getCell(colIndex);
+ if(col == null){
+ logger.error("没有找到col!tableIndex: [{}] row: [{}] col: [{}]",tableIndex,rowIndex,colIndex);
+ return this;
+ }
+ col.setColor(color);
+ return this;
+ }
+
+ /**
+ * 填充颜色
+ * @param tableIndex 第几个表格,第一个表格为0
+ * @param rowIndex 表格中的第几行,从0开始算起
+ * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB".
+ * @return
+ */
+ public WordOperator fillRowCollorTable(int tableIndex, int rowIndex, String color) {
+ // 根据角标取出表格对像
+ XWPFTable table = doc.getTables().get(tableIndex);
+ if(table == null){
+ logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,rowIndex);
+ return this;
+ }
+ XWPFTableRow row = table.getRow(rowIndex);
+ if(row == null){
+ logger.error("没有找到row!tableIndex: [{}] row: [{}]",tableIndex,rowIndex);
+ return this;
+ }
+ List cellList = row.getTableCells();
+ cellList.forEach(col -> col.setColor(color));
+ return this;
+ }
+
+ /**
+ * 填充颜色
+ * @param tableIndex 第几个表格,第一个表格为0
+ * @param colIndex 表格中的第几列,从0开始算起
+ * @param color 颜色,rgbStr - the desired cell color, in the hex form "RRGGBB".
+ * @return
+ */
+ public WordOperator fillColCollorTable(int tableIndex, int colIndex, String color) {
+ // 根据角标取出表格对像
+ XWPFTable table = doc.getTables().get(tableIndex);
+ if(table == null){
+ logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,colIndex);
+ return this;
+ }
+ List rows = table.getRows();
+ rows.forEach(row -> {
+ List cellList = row.getTableCells();
+ for (int i = 0; i < cellList.size(); i++) {
+ if(i == colIndex){
+ cellList.get(i).setColor(color);
+ }
+ }
+ });
+ return this;
+ }
+
+ /**
+ * 给定输出流即可将word文档导出到
+ */
+ public void write(OutputStream os) throws Exception {
+ doc.write(os);
+ }
+
+ /**
+ *
+ * 功能说明 : 补充空列
+ * 创建者 : byx
+ * 修改日期 : 2018年12月25日
+ * @param returnList:填充到这个list中
+ * @param inputList:输入的list,业务信息列表,用来计算空列的初始值
+ * @param endRowNum:总共N行
+ * @param colNum:拥有几列
+ */
+ public static void fillNullCall(List> returnList, List> inputList, int endRowNum, int colNum){
+ int fillNum = 0;
+ if(inputList != null && !inputList.isEmpty()){
+ fillNum = inputList.size();
+ }
+ WordOperator.fillNullCall(returnList,fillNum,endRowNum,colNum);
+
+ }
+
+ /**
+ *
+ * 功能说明 : 补充空列
+ * 创建者 : byx
+ * 修改日期 : 2018年12月25日
+ * @param returnList:填充到这个list中
+ * @param startRowNum:从N开始循环
+ * @param endRowNum:总共N行
+ * @param colNum:拥有几列
+ */
+ public static void fillNullCall(List> returnList, int startRowNum, int endRowNum, int colNum){
+ for(int i = startRowNum;i < endRowNum; i++){
+ List twoList = Lists.newArrayList();
+ for (int j = 0; j < colNum; j++) {
+ twoList.add("");
+ }
+ returnList.add(twoList);
+ }
+ }
+
+ /**
+ * 根据第几个表格删除第几行,单独删除
+ * @param tableIndex 第几个表格
+ * @param rowIndex 删除第几行
+ * @return
+ */
+ public WordOperator removeTableRow(int tableIndex, int rowIndex,int colIndex) {
+ // 根据角标取出表格对像
+ XWPFTable table = doc.getTables().get(tableIndex);
+ if(table == null){
+ logger.error("没有找到表格!tableIndex: [{}] row: [{}]",tableIndex,rowIndex);
+ return this;
+ }
+// XWPFTableRow row = table.getRow(rowIndex);
+// XWPFTableRow nextRow = table.getRow(rowIndex + 1);
+// XWPFTableCell col = row.getCell(colIndex);
+// XWPFTableCell nextCol = nextRow.getCell(colIndex);
+//
+// System.out.println("文字内容:" + col.getText());
+// List list = Lists.newArrayList();
+// list.add(StringUtils.isBlank(col.getText())?"空的??":col.getText());
+// setCellText(nextCol,list,0);
+// XWPFTableCell nextCol = nextRow.getCell(0);
+// if (nextCol.getCTTc().getTcPr() == null) nextCol.getCTTc().addNewTcPr();
+// if (nextCol.getCTTc().getTcPr().getGridSpan() == null) nextCol.getCTTc().getTcPr().addNewGridSpan();
+// nextCol.getCTTc().getTcPr().getGridSpan().setVal(rowSpan.subtract(new BigInteger("1")));
+ table.removeRow(rowIndex);
+ return this;
+ }
+
+ /**
+ * 根据第几个表格删除多行
+ * @param tableIndex 第几个表格
+ * @param rowIndex 删除第几行的集合
+ * @return
+ */
+ public WordOperator removeTableRow(int tableIndex, List rowIndex,int colIndex) {
+ rowIndex.forEach(x -> removeTableRow(tableIndex,x,colIndex));
+ return this;
+ }
+
+ /**
+ *
+ * 功能说明 : 文档再次替换时使用,因为之前已经替换一次了,所有需要将文档片段存入内存流中,在读取到文档类里,才能替换成功
+ * 创建者 : byx
+ * 修改日期 : 2018年9月26日
+ * @param wo 文档片段对象(已经{替换内容}替换过)
+ * @param result 替换的内容
+ * @return 重新替换的文档片段
+ */
+ public static WordOperator twoReplaceWord(WordOperator wo,Map result){
+ WordOperator w2o = wo;
+ try {
+ w2o = twoReplaceWord(wo);
+ w2o.replaceTextPlus(result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ //再次替换
+ return w2o;
+ }
+
+ /**
+ *
+ * 功能说明 : 文档再次替换时使用,因为之前已经替换一次了,所有需要将文档片段存入内存流中,在读取到文档类里,才能替换成功
+ * 创建者 : byx
+ * 修改日期 : 2018年9月26日
+ * @param wo 文档片段对象(已经{替换内容}替换过)
+ * @return 重新替换的文档片段
+ */
+ public static WordOperator twoReplaceWord(WordOperator wo) throws Exception {
+ //先创建内存输出流
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ //将文档先输出到
+ wo.write(output);
+ //根据内存输出流创建内存输入流
+ InputStream input = new ByteArrayInputStream(output.toByteArray());
+ //用这个输入流创建文档对象
+ WordOperator w2o = new WordOperator(input);
+ IoUtil.close(output);
+ IoUtil.close(input);
+ return w2o;
+ }
+
+}
diff --git a/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java
new file mode 100644
index 00000000..d19fc042
--- /dev/null
+++ b/jeecg-module-main/src/main/java/org/jeecg/modules/tools/word/WordTextStyle.java
@@ -0,0 +1,255 @@
+/**********************************************************************
+* $Id: WordImageLabel.java WordImageLabel ,v0.1 2018年9月19日 下午8:50:48 byx Exp $
+* Copyright ©2018 sida . All rights reserved
+***********************************************************************/
+
+package org.jeecg.modules.tools.word;
+
+
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
+import org.apache.poi.xwpf.usermodel.VerticalAlign;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHighlightColor;
+
+/**
+* 功能说明:从标签中读取键值,宽高和后缀内容
+* 创建者:byx
+* 创建时间:2018年9月19日
+*
+* 修改时间: 修改者:
+* 修改内容:
+*
+*/
+@Data
+public class WordTextStyle {
+
+ /**
+ * 颜色
+ */
+ private String color;
+
+ public void runColor(XWPFRun run){
+ if(run != null && StringUtils.isNotBlank(color)){
+ run.setColor(color);
+ }
+ }
+
+ /**
+ * 加粗
+ */
+ private Boolean isBold;
+
+ public void runBold(XWPFRun run){
+ if(run != null && isBold != null){
+ run.setBold(isBold);
+ }
+ }
+
+ /**
+ * 背景色(文本突出显示颜色)
+ */
+ private STHighlightColor.Enum bColor;
+
+ public void runBColor(XWPFRun run){
+ if(run != null && bColor != null){
+ run.getCTR().addNewRPr().addNewHighlight().setVal(bColor);
+ }
+ }
+
+ /**
+ * 底纹
+ */
+ private String shading;
+
+ public void runShading(XWPFRun run){
+ if(run != null && StringUtils.isNotBlank(shading)){
+ CTShd shd = run.getCTR().addNewRPr().addNewShd();
+ shd.setFill(shading);
+ }
+ }
+
+ /**
+ * 字体
+ */
+ private String fontFamily;
+
+ public void runFontFamily(XWPFRun run){
+ if(run != null && StringUtils.isNotBlank(fontFamily)){
+ run.setFontFamily(fontFamily);
+ }
+ }
+
+ /**
+ * 字体大小
+ */
+ private Integer fontSize;
+
+ public void runFontSize(XWPFRun run){
+ if(run != null && fontSize != null){
+ run.setFontSize(fontSize);
+ }
+ }
+
+ /**
+ * 阴影
+ */
+ private Boolean shadow;
+
+ public void runShadow(XWPFRun run){
+ if(run != null && shadow != null){
+ run.setShadow(shadow);
+ }
+ }
+
+ /**
+ * 浮雕????
+ */
+ private Boolean embossed;
+
+ public void runEmbossed(XWPFRun run){
+ if(run != null && embossed != null){
+ run.setEmbossed(embossed);
+ }
+ }
+
+ /**
+ * 删除线
+ */
+ private Boolean strikeThrough;
+
+ public void runStrikeThrough(XWPFRun run){
+ if(run != null && strikeThrough != null){
+ run.setStrikeThrough(strikeThrough);
+ }
+ }
+
+ /**
+ * 双删除线
+ */
+ private Boolean doubleStrikethrough;
+
+ public void runDoubleStrikethrough(XWPFRun run){
+ if(run != null && doubleStrikethrough != null){
+ run.setDoubleStrikethrough(doubleStrikethrough);
+ }
+ }
+
+ /**
+ * 大写????
+ */
+ private Boolean capitalized;
+
+ public void runCapitalizedg(XWPFRun run){
+ if(run != null && capitalized != null){
+ run.setCapitalized(capitalized);
+ }
+ }
+
+ /**
+ * 印记????
+ */
+ private Boolean imprinted;
+
+ public void runImprinted(XWPFRun run){
+ if(run != null && imprinted != null){
+ run.setImprinted(imprinted);
+ }
+ }
+
+ /**
+ * 斜体
+ */
+ private Boolean italic;
+
+ public void runItalic(XWPFRun run){
+ if(run != null && italic != null){
+ run.setItalic(italic);
+ }
+ }
+ /**
+ * 小型大写字母??
+ */
+ private Boolean smallCaps;
+
+ public void runSmallCaps(XWPFRun run){
+ if(run != null && smallCaps != null){
+ run.setSmallCaps(smallCaps);
+ }
+ }
+ /**
+ * 字符间距
+ */
+ private Integer kerning;
+
+ public void runKerning(XWPFRun run){
+ if(run != null && kerning != null){
+ run.setKerning(kerning);
+ }
+ }
+
+ /**
+ * 文本位置
+ */
+ private Integer textPosition;
+
+ public void runTextPosition(XWPFRun run){
+ if(run != null && textPosition != null){
+ run.setTextPosition(textPosition);
+ }
+ }
+
+ /**
+ * 下标
+ */
+ private VerticalAlign subscript;
+
+ public void runSubscript(XWPFRun run){
+ if(run != null && subscript != null){
+ run.setSubscript(subscript);
+ }
+ }
+
+ /**
+ * 下划线
+ */
+ private UnderlinePatterns underline;
+
+ public void runUnderline(XWPFRun run){
+ if(run != null && underline != null){
+ run.setUnderline(underline);
+ }
+ }
+
+ /**
+ *
+ * @param run
+ */
+ public void run(XWPFRun run){
+ runColor(run);
+ runBold(run);
+ runBColor(run);
+ runShading(run);
+ runFontFamily(run);
+ runFontSize(run);
+ runShadow(run);
+ runEmbossed(run);
+ runStrikeThrough(run);
+ runDoubleStrikethrough(run);
+ runCapitalizedg(run);
+ runImprinted(run);
+ runItalic(run);
+ runSmallCaps(run);
+ runKerning(run);
+ runTextPosition(run);
+ runSubscript(run);
+ runUnderline(run);
+
+ }
+
+
+
+
+}
diff --git a/jeecg-module-main/src/main/resources/officetemplates/exp1/tpkqk.docx b/jeecg-module-main/src/main/resources/officetemplates/exp1/tpkqk.docx
new file mode 100644
index 0000000000000000000000000000000000000000..f0bc92f74d5c83daa9947db977b4f7800aca49e9
GIT binary patch
literal 14722
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
XQ(hY4jb8u&{M!%c8$~VHe|!2rB1sWR
literal 0
HcmV?d00001
diff --git a/pom.xml b/pom.xml
index 872b0cb2..ecbf0da0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -393,6 +393,7 @@
eot
ttf
svg
+ docx