目录

  • 核心依赖
  • 动态表格
    • 测试类
    • 工具类
  • 动态数据
    • 测试类
    • 工具类

核心依赖

        <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.7</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.9</version></dependency>

动态表格

测试类

package com.uncle.demo.poi;import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.lang.System.out;/*** @program demo01* @description 测试工具类* @author: Sun Jinxin* @create: 2021/11/30 14:50*/
public class Test {public static void main(String[] args){//需要进行文本替换的信息Map<String, Object> data = new HashMap<String, Object>();data.put("name", "维维豆奶粉");//需要进行动态生成的信息Map<String,Object> mapList = new HashMap<String, Object>();//第一个动态生成的数据列表List<String[]> list01 = new ArrayList<String[]>();list01.add(new String[]{"1","维修服务","1","6100333","2020/8/4","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});list01.add(new String[]{"2","售后服务","2","6107986","2020/10/28","相关校正","设备运行正常","1.鼠标\n" + "2.头托垫\n"});list01.add(new String[]{"3","售后服务","2","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});list01.add(new String[]{"4","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});list01.add(new String[]{"5","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});list01.add(new String[]{"6","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});mapList.put("list01", list01);CustomXWPFDocument doc = WorderToNewWordUtils.changWord("C:\\Users\\sunjinxin\\Downloads\\project-office-temp-tool\\project-office-temp-tool\\demo01\\src\\main\\resources\\templates\\test1.docx",data,mapList);try (FileOutputStream fopts = new FileOutputStream("D:/呵呵.docx")) {doc.write(fopts);} catch (Exception e) {e.printStackTrace();}out.println("成功!!!");}
}

工具类

package com.uncle.demo.poi;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;import org.apache.poi.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;/*** @program demo01* @description 核心工具类* @author: Sun Jinxin* @create: 2021/11/30 14:50*/
public class WorderToNewWordUtils {/*** 根据模板生成word文档** @param inputUrl 模板路径* @param textMap  需要替换的文本内容* @param mapList  需要动态生成的内容* @return*/public static CustomXWPFDocument changWord(String inputUrl, Map<String, Object> textMap, Map<String, Object> mapList) {CustomXWPFDocument document = null;try {//获取docx解析对象document = new CustomXWPFDocument(POIXMLDocument.openPackage(inputUrl));//解析替换文本段落对象WorderToNewWordUtils.changeText(document, textMap);//解析替换表格对象WorderToNewWordUtils.changeTable(document, textMap, mapList);} catch (IOException e) {e.printStackTrace();}return document;}/*** 替换段落文本** @param document docx解析对象* @param textMap  需要替换的信息集合*/public static void changeText(CustomXWPFDocument document, Map<String, Object> textMap) {//获取段落集合List<XWPFParagraph> paragraphs = document.getParagraphs();for (XWPFParagraph paragraph : paragraphs) {//判断此段落时候需要进行替换String text = paragraph.getText();System.out.println(text);if (checkText(text)) {List<XWPFRun> runs = paragraph.getRuns();for (XWPFRun run : runs) {run.setFontSize(8);//替换模板原来位置Object ob = changeValue(run.toString(), textMap);if (ob instanceof String) {run.setText((String) ob, 0);}}}}}/*** 替换表格对象方法** @param document docx解析对象* @param textMap  需要替换的信息集合* @param mapList  需要动态生成的内容*/public static void changeTable(CustomXWPFDocument document, Map<String, Object> textMap, Map<String, Object> mapList) {//获取表格对象集合List<XWPFTable> tables = document.getTables();//循环所有需要进行替换的文本,进行替换for (int i = 0; i < tables.size(); i++) {XWPFTable table = tables.get(i);if (checkText(table.getText())) {List<XWPFTableRow> rows = table.getRows();//遍历表格,并替换模板eachTable(document, rows, textMap);}}List<String[]> list01 = (List<String[]>) mapList.get("list01");//操作word中的表格for (int i = 0; i < tables.size(); i++) {//只处理行数大于等于2的表格,且不循环表头XWPFTable table = tables.get(i);//第二个表格使用daList,插入数据//TODOif (null != list01 && 0 < list01.size() && i == 0) {//TODO 处理第几个表格insertTable(table, null, list01, 2);int initFlag = 1;int mergeRowNum = initFlag;//获取第一条数据String s1 = list01.get(0)[1];for (int j = 0; j < list01.size(); j++) {String s2 = list01.get(j)[1];if (s1.equals(s2)) {mergeRowNum++;s1 = s2;} else {s1 = s2;mergeCellVertically(table, 1, initFlag + 1, mergeRowNum);mergeCellVertically(table, 2, initFlag + 1, mergeRowNum);initFlag = mergeRowNum;mergeRowNum = initFlag + 1;}if (j == list01.size() - 1) {mergeCellVertically(table, 1, initFlag + 1, mergeRowNum);mergeCellVertically(table, 2, initFlag + 1, mergeRowNum);}}}}}/*** 遍历表格** @param rows    表格行对象* @param textMap 需要替换的信息集合*/public static void eachTable(CustomXWPFDocument document, List<XWPFTableRow> rows, Map<String, Object> textMap) {for (XWPFTableRow row : rows) {List<XWPFTableCell> cells = row.getTableCells();for (XWPFTableCell cell : cells) {//判断单元格是否需要替换if (checkText(cell.getText())) {List<XWPFParagraph> paragraphs = cell.getParagraphs();for (XWPFParagraph paragraph : paragraphs) {List<XWPFRun> runs = paragraph.getRuns();for (XWPFRun run : runs) {Object ob = changeValue(run.toString(), textMap);if (ob instanceof String) {run.setText((String) ob, 0);} else if (ob instanceof Map) {run.setText("", 0);Map pic = (Map) ob;int width = Integer.parseInt(pic.get("width").toString());int height = Integer.parseInt(pic.get("height").toString());int picType = getPictureType(pic.get("type").toString());byte[] byteArray = (byte[]) pic.get("content");ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);try {int ind = document.addPicture(byteInputStream, picType);document.createPicture(ind, width, height, paragraph);} catch (Exception e) {e.printStackTrace();}}}}}}}}/*** 为表格插入数据,行数不够添加新行** @param table     需要插入数据的表格* @param tableList 第四个表格的插入数据* @param daList    第二个表格的插入数据* @param type      表格类型:1-第一个表格 2-第二个表格 3-第三个表格 4-第四个表格*/public static void insertTable(XWPFTable table, List<String> tableList, List<String[]> daList, Integer type) {if (2 == type) {//创建行和创建需要的列for (int i = 0; i < daList.size(); i++) {//添加一个新行XWPFTableRow row = table.insertNewTableRow(i+2);for (int j = 0; j < daList.get(0).length; j++) {row.createCell();//添加第一个列}}//创建行,根据需要插入的数据添加新行,不处理表头for (int i = 0; i < daList.size(); i++) {List<XWPFTableCell> cells = table.getRow(i + 2).getTableCells();for (int j = 0; j < cells.size(); j++) {XWPFTableCell cell02 = cells.get(j);cell02.setText(daList.get(i)[j]);}}} else if (4 == type) {//插入表头下面第一行的数据for (int i = 0; i < tableList.size(); i++) {XWPFTableRow row = table.createRow();List<XWPFTableCell> cells = row.getTableCells();cells.get(0).setText(tableList.get(i));}}}/*** 判断文本中时候包含$** @param text 文本* @return 包含返回true, 不包含返回false*/public static boolean checkText(String text) {boolean check = false;if (text.indexOf("$") != -1) {check = true;}return check;}/*** 匹配传入信息集合与模板** @param value   模板需要替换的区域* @param textMap 传入信息集合* @return 模板需要替换区域信息集合对应值*/public static Object changeValue(String value, Map<String, Object> textMap) {Set<Entry<String, Object>> textSets = textMap.entrySet();Object valu = "";for (Entry<String, Object> textSet : textSets) {//匹配模板与替换值 格式${key}String key = textSet.getKey();if (value.indexOf(key) != -1) {valu = textSet.getValue();}}return valu;}/*** 将输入流中的数据写入字节数组** @param in* @return*/public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {byte[] byteArray = null;try {int total = in.available();byteArray = new byte[total];in.read(byteArray);} catch (IOException e) {e.printStackTrace();} finally {if (isClose) {try {in.close();} catch (Exception e2) {System.out.println("关闭流失败");}}}return byteArray;}/*** 根据图片类型,取得对应的图片类型代码** @param picType* @return int*/private static int getPictureType(String picType) {int res = CustomXWPFDocument.PICTURE_TYPE_PICT;if (picType != null) {if (picType.equalsIgnoreCase("png")) {res = CustomXWPFDocument.PICTURE_TYPE_PNG;} else if (picType.equalsIgnoreCase("dib")) {res = CustomXWPFDocument.PICTURE_TYPE_DIB;} else if (picType.equalsIgnoreCase("emf")) {res = CustomXWPFDocument.PICTURE_TYPE_EMF;} else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {res = CustomXWPFDocument.PICTURE_TYPE_JPEG;} else if (picType.equalsIgnoreCase("wmf")) {res = CustomXWPFDocument.PICTURE_TYPE_WMF;}}return res;}/*** 合并行** @param table* @param col     需要合并的列* @param fromRow 开始行* @param toRow   结束行*/public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {CTVMerge vmerge = CTVMerge.Factory.newInstance();if (rowIndex == fromRow) {vmerge.setVal(STMerge.RESTART);} else {vmerge.setVal(STMerge.CONTINUE);}XWPFTableCell cell = table.getRow(rowIndex).getCell(col);CTTcPr tcPr = cell.getCTTc().getTcPr();if (tcPr != null) {tcPr.setVMerge(vmerge);} else {tcPr = CTTcPr.Factory.newInstance();tcPr.setVMerge(vmerge);cell.getCTTc().setTcPr(tcPr);}}}//列合并  ,有点问题,用不了public static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {for (int colIndex = fromCol; colIndex <= toCol; colIndex++) {CTHMerge hmerge = CTHMerge.Factory.newInstance();if (colIndex == fromCol) {hmerge.setVal(STMerge.RESTART);} else {hmerge.setVal(STMerge.CONTINUE);}XWPFTableCell cell = table.getRow(row).getCell(colIndex);CTTcPr tcPr = cell.getCTTc().getTcPr();if (tcPr != null) {tcPr.setHMerge(hmerge);} else {tcPr = CTTcPr.Factory.newInstance();tcPr.setHMerge(hmerge);cell.getCTTc().setTcPr(tcPr);}}}/*** 获取需要合并单元格的下标** @return*/public static List<Integer[]> startEnd(List<String[]> daList) {List<Integer[]> indexList = new ArrayList<Integer[]>();List<String> list = new ArrayList<String>();for (int i = 0; i < daList.size(); i++) {list.add(daList.get(i)[0]);}Map<Object, Integer> tm = new HashMap<Object, Integer>();for (int i = 0; i < daList.size(); i++) {if (!tm.containsKey(daList.get(i)[0])) {tm.put(daList.get(i)[0], 1);} else {int count = tm.get(daList.get(i)[0]) + 1;tm.put(daList.get(i)[0], count);}}for (Map.Entry<Object, Integer> entry : tm.entrySet()) {String key = entry.getKey().toString();String value = entry.getValue().toString();if (list.indexOf(key) != (-1)) {Integer[] index = new Integer[2];index[0] = list.indexOf(key);index[1] = list.lastIndexOf(key);indexList.add(index);}}return indexList;}
}
package com.uncle.demo.poi;import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;/*** @program demo01* @description 自定义 XWPFDocument,并重写 createPicture()方法* @author: Sun Jinxin* @create: 2021/11/30 14:50*/
public class CustomXWPFDocument extends XWPFDocument {public CustomXWPFDocument(InputStream in) throws IOException {super(in);}public CustomXWPFDocument() {super();}public CustomXWPFDocument(OPCPackage pkg) throws IOException {super(pkg);}/*** @param id* @param width 宽* @param height 高* @param paragraph  段落*/public void createPicture(int id, int width, int height,XWPFParagraph paragraph) {final int EMU = 9525;width *= EMU;height *= EMU;String blipId = getAllPictures().get(id).getPackageRelationship().getId();CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline();System.out.println(blipId+":"+inline);String picXml = ""+ "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"+ "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"+ "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"+ "         <pic:nvPicPr>" + "            <pic:cNvPr id=\""+ id+ "\" name=\"Generated\"/>"+ "            <pic:cNvPicPr/>"+ "         </pic:nvPicPr>"+ "         <pic:blipFill>"+ "            <a:blip r:embed=\""+ blipId+ "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"+ "            <a:stretch>"+ "               <a:fillRect/>"+ "            </a:stretch>"+ "         </pic:blipFill>"+ "         <pic:spPr>"+ "            <a:xfrm>"+ "               <a:off x=\"0\" y=\"0\"/>"+ "               <a:ext cx=\""+ width+ "\" cy=\""+ height+ "\"/>"+ "            </a:xfrm>"+ "            <a:prstGeom prst=\"rect\">"+ "               <a:avLst/>"+ "            </a:prstGeom>"+ "         </pic:spPr>"+ "      </pic:pic>"+ "   </a:graphicData>" + "</a:graphic>";inline.addNewGraphic().addNewGraphicData();XmlToken xmlToken = null;try {xmlToken = XmlToken.Factory.parse(picXml);} catch (XmlException xe) {xe.printStackTrace();}inline.set(xmlToken);inline.setDistT(0);inline.setDistB(0);inline.setDistL(0);inline.setDistR(0);CTPositiveSize2D extent = inline.addNewExtent();extent.setCx(width);extent.setCy(height);CTNonVisualDrawingProps docPr = inline.addNewDocPr();docPr.setId(id);docPr.setName("图片" + id);docPr.setDescr("测试");}
}

动态数据

测试类

package com.hidata.tool;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class Test {public static void main(String[] args) throws IOException {// 存储报表全部数据Map<String, Object> wordDataMap = new HashMap<String, Object>();// 存储报表中不循环的数据Map<String, Object> parametersMap = new HashMap<String, Object>();List<Map<String, Object>> table1 = new ArrayList<Map<String, Object>>();Map<String, Object> map1=new HashMap();map1.put("name", "张三");map1.put("age", "23");map1.put("email", "12121@qq.com");Map<String, Object> map2=new HashMap();map2.put("name", "李四");map2.put("age", "45");map2.put("email", "45445@qq.com");Map<String, Object> map3=new HashMap();map3.put("name", "Tom");map3.put("age", "34");map3.put("email", "6767@qq.com");table1.add(map1);table1.add(map2);table1.add(map3);List<Map<String, Object>> table2 = new ArrayList<Map<String, Object>>();Map<String, Object> map4=new HashMap();map4.put("name", "tom");map4.put("number", "sd1234");map4.put("address", "上海");Map<String, Object> map5=new HashMap();map5.put("name", "seven");map5.put("number", "sd15678");map5.put("address", "北京");Map<String, Object> map6=new HashMap();map6.put("name", "lisa");map6.put("number", "sd9078");map6.put("address", "广州");table2.add(map4);table2.add(map5);table2.add(map6);parametersMap.put("userName", "JUVENILESS");parametersMap.put("time", "2018-03-24");parametersMap.put("sum", "3");wordDataMap.put("table1", table1);wordDataMap.put("table2", table2);wordDataMap.put("parametersMap", parametersMap);//改成你本地文件所在目录File file = new File("C:\\Users\\sunjinxin\\Desktop\\time.docx");// 读取word模板FileInputStream fileInputStream = new FileInputStream(file);WordTemplate template = new WordTemplate(fileInputStream);// 替换数据template.replaceDocument(wordDataMap);//生成文件//改成你本地文件所在目录File outputFile=new File("C:\\Users\\sunjinxin\\Desktop\\输出.docx");FileOutputStream fos  = new FileOutputStream(outputFile);template.getDocument().write(fos);}}

工具类

/*** @Title: WordTemplate2.java* @Package: com.highdata.templateTools* @Description: TODO* @author: Juveniless* @date: 2017年11月27日 下午3:23:13*/
package com.hidata.tool;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.PositionInParagraph;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;/**** 对docx文件中的文本及表格中的内容进行替换 --模板仅支持对 {key} 标签的替换** @ClassName: WordTemplate* @Description: TODO(!!!使用word2013 docx文件)* @author Juveniless* @date: 2017年11月27日 下午3:25:56* <br>(1)word模板注意页边距的问题,存在问题:比如页边距默认为3cm,画表格时,仍然可以通过* 拖拽,把表格边框拖动到看起来就像页边距只有1cm的样子,但是实际上此时页边距还是3cm,生成的* word报表的页边距还是会按照3cm来生成。解决办法,在word文件里,设置好页边距,如果需要表格* 两边页边距很窄,需要在word里设置页边距窄一点,而不是直接拖动表格边框来实现。**/public class WordTemplate {private XWPFDocument document;public XWPFDocument getDocument() {return document;}public void setDocument(XWPFDocument document) {this.document = document;}/*** 初始化模板内容** @author Juveniless* @date 2017年11月27日 下午3:59:22* @param inputStream*            模板的读取流(docx文件)* @throws IOException**/public WordTemplate(InputStream inputStream) throws IOException {document = new XWPFDocument(inputStream);}/*** 将处理后的内容写入到输出流中** @param outputStream* @throws IOException*/public void write(OutputStream outputStream) throws IOException {document.write(outputStream);}/*** 根据dataMap对word文件中的标签进行替换; <br><br>* !!!!***需要注意dataMap的数据格式***!!!! <br><br>* 对于需要替换的普通标签数据标签(不需要循环)-----必须在dataMap中存储一个key为parametersMap的map,* 来存储这些不需要循环生成的数据,比如:表头信息,日期,制表人等。 <br><br>* 对于需要循环生成的表格数据------key自定义,value为 --ArrayList&lt;Map&lt;String, String>>* @author Juveniless* @date 2017年11月27日 下午3:29:27* @param dataMap**/public void replaceDocument(Map<String, Object> dataMap) {if (!dataMap.containsKey("parametersMap")) {System.out.println("数据源错误--数据源(parametersMap)缺失");return;}@SuppressWarnings("unchecked")Map<String, Object> parametersMap = (Map<String, Object>) dataMap.get("parametersMap");List<IBodyElement> bodyElements = document.getBodyElements();// 所有对象(段落+表格)int templateBodySize = bodyElements.size();// 标记模板文件(段落+表格)总个数int curT = 0;// 当前操作表格对象的索引int curP = 0;// 当前操作段落对象的索引for (int a = 0; a < templateBodySize; a++) {IBodyElement body = bodyElements.get(a);if (BodyElementType.TABLE.equals(body.getElementType())) {// 处理表格XWPFTable table = body.getBody().getTableArray(curT);List<XWPFTable> tables = body.getBody().getTables();table = tables.get(curT);if (table != null) {// 处理表格List<XWPFTableCell> tableCells = table.getRows().get(0).getTableCells();// 获取到模板表格第一行,用来判断表格类型String tableText = table.getText();// 表格中的所有文本if (tableText.indexOf("##{foreach") > -1) {// 查找到##{foreach标签,该表格需要处理循环if (tableCells.size() != 2|| tableCells.get(0).getText().indexOf("##{foreach") < 0|| tableCells.get(0).getText().trim().length() == 0) {System.out.println("文档中第"+ (curT + 1)+ "个表格模板错误,模板表格第一行需要设置2个单元格,"+ "第一个单元格存储表格类型(##{foreachTable}## 或者 ##{foreachTableRow}##),第二个单元格定义数据源。");return;}String tableType = tableCells.get(0).getText();String dataSource = tableCells.get(1).getText();System.out.println("读取到数据源:"+dataSource);if (!dataMap.containsKey(dataSource)) {System.out.println("文档中第" + (curT + 1) + "个表格模板数据源缺失");return;}@SuppressWarnings("unchecked")List<Map<String, Object>> tableDataList = (List<Map<String, Object>>) dataMap.get(dataSource);if ("##{foreachTable}##".equals(tableType)) {// System.out.println("循环生成表格");addTableInDocFooter(table, tableDataList, parametersMap, 1);} else if ("##{foreachTableRow}##".equals(tableType)) {// System.out.println("循环生成表格内部的行");addTableInDocFooter(table, tableDataList, parametersMap, 2);}} else if (tableText.indexOf("{") > -1) {// 没有查找到##{foreach标签,查找到了普通替换数据的{}标签,该表格只需要简单替换addTableInDocFooter(table, null, parametersMap, 3);} else {// 没有查找到任何标签,该表格是一个静态表格,仅需要复制一个即可。addTableInDocFooter(table, null, null, 0);}curT++;}} else if (BodyElementType.PARAGRAPH.equals(body.getElementType())) {// 处理段落// System.out.println("获取到段落");XWPFParagraph ph = body.getBody().getParagraphArray(curP);if (ph != null) {// htmlText = htmlText+readParagraphX(ph);addParagraphInDocFooter(ph, null, parametersMap, 0);curP++;}}}// 处理完毕模板,删除文本中的模板内容for (int a = 0; a < templateBodySize; a++) {document.removeBodyElement(0);}}/*** 根据 模板表格 和 数据list 在word文档末尾生成表格* @author Juveniless* @date 2017年12月6日 上午10:12:05* @param templateTable 模板表格* @param list   循环数据集* @param parametersMap  不循环数据集* @param flag   (0为静态表格,1为表格整体循环,2为表格内部行循环,3为表格不循环仅简单替换标签即可)**/public void addTableInDocFooter(XWPFTable templateTable, List<Map<String, Object>> list,Map<String, Object> parametersMap, int flag) {if (flag == 1) {// 表格整体循环for (Map<String, Object> map : list) {List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列for (int i = 1; i < templateTableRows.size(); i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行}newCreateTable.removeRow(0);// 移除多出来的第一行document.createParagraph();// 添加回车换行replaceTable(newCreateTable, map);//替换标签}} else if (flag == 2) {// 表格表格内部行循环XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列List<XWPFTableRow> TempTableRows = templateTable.getRows();// 获取模板表格所有行int tagRowsIndex = 0;// 标签行indexsfor (int i = 0, size = TempTableRows.size(); i < size; i++) {String rowText = TempTableRows.get(i).getCell(0).getText();// 获取到表格行的第一个单元格if (rowText.indexOf("##{foreachRows}##") > -1) {tagRowsIndex = i;break;}}/* 复制模板行和标签行之前的行 */for (int i = 1; i < tagRowsIndex; i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, TempTableRows.get(i));// 复制行replaceTableRow(newCreateRow, parametersMap);// 处理不循环标签的替换}/* 循环生成模板行 */XWPFTableRow tempRow = TempTableRows.get(tagRowsIndex + 1);// 获取到模板行for (int i = 0; i < list.size(); i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, tempRow);// 复制模板行replaceTableRow(newCreateRow, list.get(i));// 处理标签替换}/* 复制模板行和标签行之后的行 */for (int i = tagRowsIndex + 2; i < TempTableRows.size(); i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, TempTableRows.get(i));// 复制行replaceTableRow(newCreateRow, parametersMap);// 处理不循环标签的替换}newCreateTable.removeRow(0);// 移除多出来的第一行document.createParagraph();// 添加回车换行} else if (flag == 3) {//表格不循环仅简单替换标签List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列for (int i = 0; i < templateTableRows.size(); i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行}newCreateTable.removeRow(0);// 移除多出来的第一行document.createParagraph();// 添加回车换行replaceTable(newCreateTable, parametersMap);} else if (flag == 0) {List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列for (int i = 0; i < templateTableRows.size(); i++) {XWPFTableRow newCreateRow = newCreateTable.createRow();CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行}newCreateTable.removeRow(0);// 移除多出来的第一行document.createParagraph();// 添加回车换行}}/*** 根据 模板段落 和 数据 在文档末尾生成段落** @author Juveniless* @date 2017年11月27日 上午11:49:42* @param templateParagraph*            模板段落* @param list*            循环数据集* @param parametersMap*            不循环数据集* @param flag*            (0为不循环替换,1为循环替换)**/public void addParagraphInDocFooter(XWPFParagraph templateParagraph,List<Map<String, String>> list, Map<String, Object> parametersMap, int flag) {if (flag == 0) {XWPFParagraph createParagraph = document.createParagraph();// 设置段落样式createParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());// 移除原始内容for (int pos = 0; pos < createParagraph.getRuns().size(); pos++) {createParagraph.removeRun(pos);}// 添加Run标签for (XWPFRun s : templateParagraph.getRuns()) {XWPFRun targetrun = createParagraph.createRun();CopyRun(targetrun, s);}replaceParagraph(createParagraph, parametersMap);} else if (flag == 1) {// 暂无实现}}/*** 根据map替换段落元素内的{**}标签* @author Juveniless* @date 2017年12月4日 下午3:09:00* @param xWPFParagraph* @param parametersMap**/public void replaceParagraph(XWPFParagraph xWPFParagraph, Map<String, Object> parametersMap) {List<XWPFRun> runs = xWPFParagraph.getRuns();String xWPFParagraphText = xWPFParagraph.getText();String regEx = "\\{.+?\\}";Pattern pattern = Pattern.compile(regEx);Matcher matcher = pattern.matcher(xWPFParagraphText);//正则匹配字符串{****}if (matcher.find()) {// 查找到有标签才执行替换int beginRunIndex = xWPFParagraph.searchText("{", new PositionInParagraph()).getBeginRun();// 标签开始run位置int endRunIndex = xWPFParagraph.searchText("}", new PositionInParagraph()).getEndRun();// 结束标签StringBuffer key = new StringBuffer();if (beginRunIndex == endRunIndex) {// {**}在一个run标签内XWPFRun beginRun = runs.get(beginRunIndex);String beginRunText = beginRun.text();int beginIndex = beginRunText.indexOf("{");int endIndex = beginRunText.indexOf("}");int length = beginRunText.length();if (beginIndex == 0 && endIndex == length - 1) {// 该run标签只有{**}XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());// 设置文本key.append(beginRunText.substring(1, endIndex));insertNewRun.setText(getValueBykey(key.toString(),parametersMap));xWPFParagraph.removeRun(beginRunIndex + 1);} else {// 该run标签为**{**}** 或者 **{**} 或者{**}**,替换key后,还需要加上原始key前后的文本XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());// 设置文本key.append(beginRunText.substring(beginRunText.indexOf("{")+1, beginRunText.indexOf("}")));String textString=beginRunText.substring(0, beginIndex) + getValueBykey(key.toString(),parametersMap)+ beginRunText.substring(endIndex + 1);insertNewRun.setText(textString);xWPFParagraph.removeRun(beginRunIndex + 1);}}else {// {**}被分成多个run//先处理起始run标签,取得第一个{key}值XWPFRun beginRun = runs.get(beginRunIndex);String beginRunText = beginRun.text();int beginIndex = beginRunText.indexOf("{");if (beginRunText.length()>1  ) {key.append(beginRunText.substring(beginIndex+1));}ArrayList<Integer> removeRunList = new ArrayList();//需要移除的run//处理中间的runfor (int i = beginRunIndex + 1; i < endRunIndex; i++) {XWPFRun run = runs.get(i);String runText = run.text();key.append(runText);removeRunList.add(i);}// 获取endRun中的key值XWPFRun endRun = runs.get(endRunIndex);String endRunText = endRun.text();int endIndex = endRunText.indexOf("}");//run中**}或者**}**if (endRunText.length()>1 && endIndex!=0) {key.append(endRunText.substring(0,endIndex));}//*******************************************************************//取得key值后替换标签//先处理开始标签if (beginRunText.length()==2 ) {// run标签内文本{XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());// 设置文本insertNewRun.setText(getValueBykey(key.toString(),parametersMap));xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run}else {// 该run标签为**{**或者 {** ,替换key后,还需要加上原始key前的文本XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());// 设置文本String textString=beginRunText.substring(0,beginRunText.indexOf("{"))+getValueBykey(key.toString(),parametersMap);insertNewRun.setText(textString);xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run}//处理结束标签if (endRunText.length()==1 ) {// run标签内文本只有}XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());// 设置文本insertNewRun.setText("");xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run}else {// 该run标签为**}**或者 }** 或者**},替换key后,还需要加上原始key后的文本XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());// 设置文本String textString=endRunText.substring(endRunText.indexOf("}")+1);insertNewRun.setText(textString);xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run}//处理中间的run标签for (int i = 0; i < removeRunList.size(); i++) {XWPFRun xWPFRun = runs.get(removeRunList.get(i));//原始runXWPFRun insertNewRun = xWPFParagraph.insertNewRun(removeRunList.get(i));insertNewRun.getCTR().setRPr(xWPFRun.getCTR().getRPr());insertNewRun.setText("");xWPFParagraph.removeRun(removeRunList.get(i) + 1);//移除原始的run}}// 处理${**}被分成多个runreplaceParagraph( xWPFParagraph, parametersMap);}//if 有标签}/*** 复制表格行XWPFTableRow格式** @param target*            待修改格式的XWPFTableRow* @param source*            模板XWPFTableRow*/private void CopyTableRow(XWPFTableRow target, XWPFTableRow source) {int tempRowCellsize = source.getTableCells().size();// 模板行的列数for (int i = 0; i < tempRowCellsize - 1; i++) {target.addNewTableCell();// 为新添加的行添加与模板表格对应行行相同个数的单元格}// 复制样式target.getCtRow().setTrPr(source.getCtRow().getTrPr());// 复制单元格for (int i = 0; i < target.getTableCells().size(); i++) {copyTableCell(target.getCell(i), source.getCell(i));}}/*** 复制单元格XWPFTableCell格式** @author Juveniless* @date 2017年11月27日 下午3:41:02* @param newTableCell*            新创建的的单元格* @param templateTableCell*            模板单元格**/private void copyTableCell(XWPFTableCell newTableCell, XWPFTableCell templateTableCell) {// 列属性newTableCell.getCTTc().setTcPr(templateTableCell.getCTTc().getTcPr());// 删除目标 targetCell 所有文本段落for (int pos = 0; pos < newTableCell.getParagraphs().size(); pos++) {newTableCell.removeParagraph(pos);}// 添加新文本段落for (XWPFParagraph sp : templateTableCell.getParagraphs()) {XWPFParagraph targetP = newTableCell.addParagraph();copyParagraph(targetP, sp);}}/*** 复制文本段落XWPFParagraph格式** @author Juveniless* @date 2017年11月27日 下午3:43:08* @param newParagraph*            新创建的的段落* @param templateParagraph*            模板段落**/private void copyParagraph(XWPFParagraph newParagraph, XWPFParagraph templateParagraph) {// 设置段落样式newParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());// 添加Run标签for (int pos = 0; pos < newParagraph.getRuns().size(); pos++) {newParagraph.removeRun(pos);}for (XWPFRun s : templateParagraph.getRuns()) {XWPFRun targetrun = newParagraph.createRun();CopyRun(targetrun, s);}}/*** 复制文本节点run* @author Juveniless* @date 2017年11月27日 下午3:47:17* @param newRun*            新创建的的文本节点* @param templateRun*            模板文本节点**/private void CopyRun(XWPFRun newRun, XWPFRun templateRun) {newRun.getCTR().setRPr(templateRun.getCTR().getRPr());// 设置文本newRun.setText(templateRun.text());}/*** 根据参数parametersMap对表格的一行进行标签的替换** @author Juveniless* @date 2017年11月23日 下午2:09:24* @param tableRow*            表格行* @param parametersMap*            参数map**/public void replaceTableRow(XWPFTableRow tableRow, Map<String, Object> parametersMap) {List<XWPFTableCell> tableCells = tableRow.getTableCells();for (XWPFTableCell xWPFTableCell : tableCells) {List<XWPFParagraph> paragraphs = xWPFTableCell.getParagraphs();for (XWPFParagraph xwpfParagraph : paragraphs) {replaceParagraph(xwpfParagraph, parametersMap);}}}/*** 根据map替换表格中的{key}标签* @author Juveniless* @date 2017年12月4日 下午2:47:36* @param xwpfTable* @param parametersMap**/public void replaceTable(XWPFTable xwpfTable,Map<String, Object> parametersMap){List<XWPFTableRow> rows = xwpfTable.getRows();for (XWPFTableRow xWPFTableRow : rows ) {List<XWPFTableCell> tableCells = xWPFTableRow.getTableCells();for (XWPFTableCell xWPFTableCell : tableCells ) {List<XWPFParagraph> paragraphs2 = xWPFTableCell.getParagraphs();for (XWPFParagraph xWPFParagraph : paragraphs2) {replaceParagraph(xWPFParagraph, parametersMap);}}}}private String getValueBykey(String key, Map<String, Object> map) {String returnValue="";if (key != null) {try {returnValue=map.get(key)!=null ? map.get(key).toString() : "";} catch (Exception e) {// TODO: handle exceptionSystem.out.println("key:"+key+"***"+e);returnValue="";}}return returnValue;}
}

POI利用word模板动态生成word报表以及动态生成word表格相关推荐

  1. android 生成多个表单,Android根据word模板文档将表单数据生成word文档的方案整理...

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 尝试的方案包括以下几种: freemarker 只能在java项目上运行,无法在Android项目上运行: poi 解析doc文件可 ...

  2. Java-POI替换Word模板文档中的变量,生成Word文档

    第一步:在项目中导入POI的jar包,以及相关的jar包. 所需的jar包列表: 下载地址:http://download.csdn.net/download/qq_34908167/10046670 ...

  3. 易语言word模板复制粘贴_请给你的Word“打底妆”(让Word跑快些系列)

    作者:发愤的草莓 链接:https://www.jianshu.com/p/b48762d8b333 如果用Word写长文时,你是不是习惯直接打开一份空白页面,然后开始敲打键盘,等到写完调整格式时,发 ...

  4. python套用论文word模板_如何将一篇论文的word模板应用到另一篇论文上?

    这个我来答,缓缓,我慢慢来 保留原格式复制入.格式刷,可达到效果,但效率.效果皆差,且LOW 分为3个步骤: 1-模板论文B中设置样式集, 2-将论文B中各样式导入本次论文A, 3-论文A中对应设置各 ...

  5. 使用PHP将Word文档转化为pdf文件(用户提交数据到Word模板并修改部分内容,再将Word转为PDF)

    需要实现的功能: 1.用户提交信息,例如:名称,年龄,电话等等 2.将这些信息填充到固定好的word模板中. 3.将生成的word转化为PDF. 4.使用的语言PHP 开始demo.docx文件中的内 ...

  6. oracle生成excle报表,oracle自动生成excel报表(时间为变量,条件含变量)

    目的:unix+oracle下每日生成前一天某统计报表,以一张统计表为例. ----在unix下执行sh脚本---- sqlplus -s username/password < @/home/ ...

  7. java基于word模板动态生成word及转pdf实践

    在项目中很容易会遇到需要动态生成pdf的应用场景,其实现方式也比较多 由于项目的关系,对于这种组件性的开发方式我想的是怎么方便怎么来,怎么快就怎么来 在咨询了之前做政务系统的同学后,他们都一致推荐我使 ...

  8. JAVA使用POI-TL通过Word模板生成Word文件

    技术背景 很多时候我们网站或者系统需要提供一些word文件,例如证明.docx或者订单.docx等文件供用户下载打印等. 用Java操作word文档,毫无疑问,当下最流行apache poi,对于po ...

  9. asposeword.dll通过word模板生成word、PDF

    效果图 1.word模板(部分)书签 2.生成结果图 开始上代码 Dictionary<string, string> dictSource = new Dictionary<str ...

  10. Java按照word模板导出、下载文档

    在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表.医院的患者统计报表.电商平台的销售报表等等. 导出Word方式多种多样,通常有以下几种方式: 1. 使用第三方Java ...

最新文章

  1. [翻译]Json.NET API-Linq to Json Basic Operator(基本操作)
  2. 关于ES、PES、PS以及TS码流
  3. 学习Qt的资源-网站、论坛、博客等
  4. golang文件操作
  5. spring面向AOP之动态代理
  6. php过滤危险路径,PHP技巧:php过滤危险html代码
  7. 20200703:将有序数组转换为二叉搜索树(leetcode108)
  8. Intel微处理器列表_百度百科
  9. js udp通信_Node.JS实战17:开发一个正向代理服务器
  10. Aitit aop之道 艾龙著 需要整合zop资料包东西 第1章 AOP的产生         1.1 软件开发方法的演进         1.2 AOP产生的前奏——把系统看做一批关注点
  11. Java中如何使某个类的对象唯一性,只能创建一个对象
  12. 人工智能数据集文本处理Onehot/TF/TFIDF矩阵的生成
  13. PS制作水彩效果头像
  14. 2.2磁盘IO网络IO工作机制
  15. 2021年度汇总丨20大热门TWS耳机功能配置汇总解析
  16. Unity实现UI的边缘检测和拖拽拉伸功能
  17. 一文了解线控制动市场格局——7家公司10款产品盘点
  18. git最佳实践之feature和hotfix分支
  19. Coding Interview University学习
  20. spring配置方式:

热门文章

  1. VMware workstation的三种网络模式
  2. linux上nagios安装完整版
  3. C++基础-郑莉 11-12章
  4. 2018 Mossad Challenge
  5. java计算机毕业设计在线影视系统源码+数据库+lw文档+系统+部署
  6. java毕业设计短视频网站mybatis+源码+调试部署+系统+数据库+lw
  7. SWUSTOJ #971 统计利用先序遍历创建的二叉树的深度
  8. Phishing钓鱼邮件平台搭建
  9. c语言signed int与unsigned int的运算
  10. 修改本地Git用户名、密码