poi根据模板导出word(包含图片、动态生成表格、合并单元格)

模板样式:

poi根据模板导出word(包含图片、动态生成表格、合并单元格)

运行结果:

poi根据模板导出word(包含图片、动态生成表格、合并单元格)


需要的jar包:

  1. <!-- poi Excel、Word操作-->
  2. <dependency>
  3. <groupId>org.apache.poi</groupId>
  4. <artifactId>poi</artifactId>
  5. <version>3.9</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.apache.poi</groupId>
  9. <artifactId>poi-ooxml</artifactId>
  10. <version>3.7</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.apache.poi</groupId>
  14. <artifactId>poi-ooxml-schemas</artifactId>
  15. <version>3.9</version>
  16. </dependency>

文件目录结构:

poi根据模板导出word(包含图片、动态生成表格、合并单元格)

Test类:

  1. package cn.gl.word;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. public class Test {
  9. public static void main(String[] args) throws Exception {
  10. //需要进行文本替换的信息
  11. Map<String, Object> data = new HashMap<String, Object>();
  12. data.put("${date}", "2018-03-06");
  13. data.put("${name}", "东方明珠");
  14. data.put("${address}", "上海黄浦江附近");
  15. data.put("${communityValue}", "");
  16. data.put("${safetyCode}", "东方社区");
  17. data.put("${picture2}", "");
  18. data.put("${picture3}", "");
  19. data.put("${buildingValue2}", "漫展提示");
  20. data.put("${patrolPhoto1}", "");
  21. data.put("${patrolPhoto2}", "");
  22. data.put("${buildingValue3}", "中国标语");
  23. //图片,如果是多个图片,就新建多个map
  24. Map<String,Object> picture1 = new HashMap<String, Object>();
  25. picture1.put("width", 100);
  26. picture1.put("height", 150);
  27. picture1.put("type", "jpg");
  28. picture1.put("content", WorderToNewWordUtils.inputStream2ByteArray(new FileInputStream("template/c1.jpg"), true));
  29. data.put("${picture1}",picture1);
  30. //需要进行动态生成的信息
  31. Map<String,Object> mapList = new HashMap<String, Object>();
  32. //第一个动态生成的数据列表
  33. List<String[]> list01 = new ArrayList<String[]>();
  34. list01.add(new String[]{"A","美女好看"});
  35. list01.add(new String[]{"A","美女好多"});
  36. list01.add(new String[]{"B","漫展人太多"});
  37. list01.add(new String[]{"C","妹子穿的很清凉"});
  38. //第二个动态生成的数据列表
  39. List<String> list02 = new ArrayList<String>();
  40. list02.add("1、*");
  41. list02.add("2、富强");
  42. list02.add("3、文明");
  43. list02.add("4、和谐");
  44. mapList.put("list01", list01);
  45. mapList.put("list02", list02);
  46. CustomXWPFDocument doc = WorderToNewWordUtils.changWord("template/demo.docx",data,mapList);
  47. FileOutputStream fopts = new FileOutputStream("D:/呵呵.docx");
  48. doc.write(fopts);
  49. fopts.close();
  50. }
  51. }

WorderToNewWordUtils类:

  1. package cn.gl.word;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.OutputStream;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Map.Entry;
  11. import java.util.Set;
  12. import org.apache.poi.POIXMLDocument;
  13. import org.apache.poi.xwpf.usermodel.XWPFParagraph;
  14. import org.apache.poi.xwpf.usermodel.XWPFRun;
  15. import org.apache.poi.xwpf.usermodel.XWPFTable;
  16. import org.apache.poi.xwpf.usermodel.XWPFTableCell;
  17. import org.apache.poi.xwpf.usermodel.XWPFTableRow;
  18. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge;
  19. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
  20. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
  21. import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
  22. public class WorderToNewWordUtils {
  23. /**
  24. * 根据模板生成word文档
  25. * @param inputUrl 模板路径
  26. * @param textMap 需要替换的文本内容
  27. * @param mapList 需要动态生成的内容
  28. * @return
  29. */
  30. public static CustomXWPFDocument changWord(String inputUrl, Map<String, Object> textMap, Map<String, Object> mapList) {
  31. CustomXWPFDocument document = null;
  32. try {
  33. //获取docx解析对象
  34. document = new CustomXWPFDocument(POIXMLDocument.openPackage(inputUrl));
  35. //解析替换文本段落对象
  36. WorderToNewWordUtils.changeText(document, textMap);
  37. //解析替换表格对象
  38. WorderToNewWordUtils.changeTable(document, textMap, mapList);
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. return document;
  43. }
  44. /**
  45. * 替换段落文本
  46. * @param document docx解析对象
  47. * @param textMap 需要替换的信息集合
  48. */
  49. public static void changeText(CustomXWPFDocument document, Map<String, Object> textMap){
  50. //获取段落集合
  51. List<XWPFParagraph> paragraphs = document.getParagraphs();
  52. for (XWPFParagraph paragraph : paragraphs) {
  53. //判断此段落时候需要进行替换
  54. String text = paragraph.getText();
  55. System.out.println(text);
  56. if(checkText(text)){
  57. List<XWPFRun> runs = paragraph.getRuns();
  58. for (XWPFRun run : runs) {
  59. //替换模板原来位置
  60. Object ob = changeValue(run.toString(), textMap);
  61. if (ob instanceof String){
  62. run.setText((String)ob,0);
  63. }
  64. }
  65. }
  66. }
  67. }
  68. /**
  69. * 替换表格对象方法
  70. * @param document docx解析对象
  71. * @param textMap 需要替换的信息集合
  72. * @param mapList 需要动态生成的内容
  73. */
  74. public static void changeTable(CustomXWPFDocument document, Map<String, Object> textMap,Map<String, Object> mapList){
  75. //获取表格对象集合
  76. List<XWPFTable> tables = document.getTables();
  77. //循环所有需要进行替换的文本,进行替换
  78. for (int i = 0; i < tables.size(); i++) {
  79. XWPFTable table = tables.get(i);
  80. if(checkText(table.getText())){
  81. List<XWPFTableRow> rows = table.getRows();
  82. //遍历表格,并替换模板
  83. eachTable(document,rows, textMap);
  84. }
  85. }
  86. List<String[]> list01 = (List<String[]>) mapList.get("list01");
  87. List<String> list02 = (List<String>) mapList.get("list02");
  88. //操作word中的表格
  89. for (int i = 0; i < tables.size(); i++) {
  90. //只处理行数大于等于2的表格,且不循环表头
  91. XWPFTable table = tables.get(i);
  92. //第二个表格使用daList,插入数据
  93. if (null != list01 && 0 < list01.size() && i == 1){
  94. insertTable(table, null,list01,2);
  95. List<Integer[]> indexList = startEnd(list01);
  96. for (int c=0;c<indexList.size();c++){
  97. //合并行
  98. mergeCellVertically(table,0,indexList.get(c)[0]+1,indexList.get(c)[1]+1);
  99. }
  100. }
  101. //第四个表格使用tableList,插入数据
  102. if (null != list02 && 0 < list02.size() && i == 3){
  103. insertTable(table, list02,null,4);
  104. }
  105. }
  106. }
  107. /**
  108. * 遍历表格
  109. * @param rows 表格行对象
  110. * @param textMap 需要替换的信息集合
  111. */
  112. public static void eachTable(CustomXWPFDocument document,List<XWPFTableRow> rows ,Map<String, Object> textMap){
  113. for (XWPFTableRow row : rows) {
  114. List<XWPFTableCell> cells = row.getTableCells();
  115. for (XWPFTableCell cell : cells) {
  116. //判断单元格是否需要替换
  117. if(checkText(cell.getText())){
  118. List<XWPFParagraph> paragraphs = cell.getParagraphs();
  119. for (XWPFParagraph paragraph : paragraphs) {
  120. List<XWPFRun> runs = paragraph.getRuns();
  121. for (XWPFRun run : runs) {
  122. Object ob = changeValue(run.toString(), textMap);
  123. if (ob instanceof String){
  124. run.setText((String)ob,0);
  125. }else if (ob instanceof Map){
  126. run.setText("",0);
  127. Map pic = (Map)ob;
  128. int width = Integer.parseInt(pic.get("width").toString());
  129. int height = Integer.parseInt(pic.get("height").toString());
  130. int picType = getPictureType(pic.get("type").toString());
  131. byte[] byteArray = (byte[]) pic.get("content");
  132. ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
  133. try {
  134. int ind = document.addPicture(byteInputStream,picType);
  135. document.createPicture(ind, width , height,paragraph);
  136. } catch (Exception e) {
  137. e.printStackTrace();
  138. }
  139. }
  140. }
  141. }
  142. }
  143. }
  144. }
  145. }
  146. /**
  147. * 为表格插入数据,行数不够添加新行
  148. * @param table 需要插入数据的表格
  149. * @param tableList 第四个表格的插入数据
  150. * @param daList 第二个表格的插入数据
  151. * @param type 表格类型:1-第一个表格 2-第二个表格 3-第三个表格 4-第四个表格
  152. */
  153. public static void insertTable(XWPFTable table, List<String> tableList,List<String[]> daList,Integer type){
  154. if (2 == type){
  155. //创建行和创建需要的列
  156. for(int i = 1; i < daList.size(); i++){
  157. XWPFTableRow row = table.insertNewTableRow(1);//添加一个新行
  158. row.createCell();//添加第一个列
  159. row.createCell();//添加第二个列
  160. }
  161. //创建行,根据需要插入的数据添加新行,不处理表头
  162. for(int i = 0; i < daList.size(); i++){
  163. List<XWPFTableCell> cells = table.getRow(i+1).getTableCells();
  164. for(int j = 0; j < cells.size(); j++){
  165. XWPFTableCell cell02 = cells.get(j);
  166. cell02.setText(daList.get(i)[j]);
  167. }
  168. }
  169. }else if (4 == type){
  170. //插入表头下面第一行的数据
  171. for(int i = 0; i < tableList.size(); i++){
  172. XWPFTableRow row = table.createRow();
  173. List<XWPFTableCell> cells = row.getTableCells();
  174. cells.get(0).setText(tableList.get(i));
  175. }
  176. }
  177. }
  178. /**
  179. * 判断文本中时候包含$
  180. * @param text 文本
  181. * @return 包含返回true,不包含返回false
  182. */
  183. public static boolean checkText(String text){
  184. boolean check = false;
  185. if(text.indexOf("$")!= -1){
  186. check = true;
  187. }
  188. return check;
  189. }
  190. /**
  191. * 匹配传入信息集合与模板
  192. * @param value 模板需要替换的区域
  193. * @param textMap 传入信息集合
  194. * @return 模板需要替换区域信息集合对应值
  195. */
  196. public static Object changeValue(String value, Map<String, Object> textMap){
  197. Set<Entry<String, Object>> textSets = textMap.entrySet();
  198. Object valu = "";
  199. for (Entry<String, Object> textSet : textSets) {
  200. //匹配模板与替换值 格式${key}
  201. String key = textSet.getKey();
  202. if(value.indexOf(key)!= -1){
  203. valu = textSet.getValue();
  204. }
  205. }
  206. return valu;
  207. }
  208. /**
  209. * 将输入流中的数据写入字节数组
  210. * @param in
  211. * @return
  212. */
  213. public static byte[] inputStream2ByteArray(InputStream in,boolean isClose){
  214. byte[] byteArray = null;
  215. try {
  216. int total = in.available();
  217. byteArray = new byte[total];
  218. in.read(byteArray);
  219. } catch (IOException e) {
  220. e.printStackTrace();
  221. }finally{
  222. if(isClose){
  223. try {
  224. in.close();
  225. } catch (Exception e2) {
  226. System.out.println("关闭流失败");
  227. }
  228. }
  229. }
  230. return byteArray;
  231. }
  232. /**
  233. * 根据图片类型,取得对应的图片类型代码
  234. * @param picType
  235. * @return int
  236. */
  237. private static int getPictureType(String picType){
  238. int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
  239. if(picType != null){
  240. if(picType.equalsIgnoreCase("png")){
  241. res = CustomXWPFDocument.PICTURE_TYPE_PNG;
  242. }else if(picType.equalsIgnoreCase("dib")){
  243. res = CustomXWPFDocument.PICTURE_TYPE_DIB;
  244. }else if(picType.equalsIgnoreCase("emf")){
  245. res = CustomXWPFDocument.PICTURE_TYPE_EMF;
  246. }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
  247. res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
  248. }else if(picType.equalsIgnoreCase("wmf")){
  249. res = CustomXWPFDocument.PICTURE_TYPE_WMF;
  250. }
  251. }
  252. return res;
  253. }
  254. /**
  255. * 合并行
  256. * @param table
  257. * @param col 需要合并的列
  258. * @param fromRow 开始行
  259. * @param toRow 结束行
  260. */
  261. public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
  262. for(int rowIndex = fromRow; rowIndex <= toRow; rowIndex++){
  263. CTVMerge vmerge = CTVMerge.Factory.newInstance();
  264. if(rowIndex == fromRow){
  265. vmerge.setVal(STMerge.RESTART);
  266. } else {
  267. vmerge.setVal(STMerge.CONTINUE);
  268. }
  269. XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
  270. CTTcPr tcPr = cell.getCTTc().getTcPr();
  271. if (tcPr != null) {
  272. tcPr.setVMerge(vmerge);
  273. } else {
  274. tcPr = CTTcPr.Factory.newInstance();
  275. tcPr.setVMerge(vmerge);
  276. cell.getCTTc().setTcPr(tcPr);
  277. }
  278. }
  279. }
  280. //列合并 ,有点问题,用不了
  281. public static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
  282. for(int colIndex = fromCol; colIndex <= toCol; colIndex++){
  283. CTHMerge hmerge = CTHMerge.Factory.newInstance();
  284. if(colIndex == fromCol){
  285. hmerge.setVal(STMerge.RESTART);
  286. } else {
  287. hmerge.setVal(STMerge.CONTINUE);
  288. }
  289. XWPFTableCell cell = table.getRow(row).getCell(colIndex);
  290. CTTcPr tcPr = cell.getCTTc().getTcPr();
  291. if (tcPr != null) {
  292. tcPr.setHMerge(hmerge);
  293. } else {
  294. tcPr = CTTcPr.Factory.newInstance();
  295. tcPr.setHMerge(hmerge);
  296. cell.getCTTc().setTcPr(tcPr);
  297. }
  298. }
  299. }
  300. /**
  301. * 获取需要合并单元格的下标
  302. * @return
  303. */
  304. public static List<Integer[]> startEnd(List<String[]> daList){
  305. List<Integer[]> indexList = new ArrayList<Integer[]>();
  306. List<String> list = new ArrayList<String>();
  307. for (int i=0;i<daList.size();i++){
  308. list.add(daList.get(i)[0]);
  309. }
  310. Map<Object, Integer> tm = new HashMap<Object, Integer>();
  311. for (int i=0;i<daList.size();i++){
  312. if (!tm.containsKey(daList.get(i)[0])) {
  313. tm.put(daList.get(i)[0], 1);
  314. } else {
  315. int count = tm.get(daList.get(i)[0]) + 1;
  316. tm.put(daList.get(i)[0], count);
  317. }
  318. }
  319. for (Map.Entry<Object, Integer> entry : tm.entrySet()) {
  320. String key = entry.getKey().toString();
  321. String value = entry.getValue().toString();
  322. if (list.indexOf(key) != (-1)){
  323. Integer[] index = new Integer[2];
  324. index[0] = list.indexOf(key);
  325. index[1] = list.lastIndexOf(key);
  326. indexList.add(index);
  327. }
  328. }
  329. return indexList;
  330. }
  331. }

CustomXWPFDocument类:

  1. package cn.gl.word;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import org.apache.poi.openxml4j.opc.OPCPackage;
  5. import org.apache.poi.xwpf.usermodel.XWPFDocument;
  6. import org.apache.poi.xwpf.usermodel.XWPFParagraph;
  7. import org.apache.xmlbeans.XmlException;
  8. import org.apache.xmlbeans.XmlToken;
  9. import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
  10. import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
  11. import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
  12. /**
  13. * 自定义 XWPFDocument,并重写 createPicture()方法
  14. */
  15. public class CustomXWPFDocument extends XWPFDocument {
  16. public CustomXWPFDocument(InputStream in) throws IOException {
  17. super(in);
  18. }
  19. public CustomXWPFDocument() {
  20. super();
  21. }
  22. public CustomXWPFDocument(OPCPackage pkg) throws IOException {
  23. super(pkg);
  24. }
  25. /**
  26. * @param id
  27. * @param width 宽
  28. * @param height 高
  29. * @param paragraph 段落
  30. */
  31. public void createPicture(int id, int width, int height,XWPFParagraph paragraph) {
  32. final int EMU = 9525;
  33. width *= EMU;
  34. height *= EMU;
  35. String blipId = getAllPictures().get(id).getPackageRelationship().getId();
  36. CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline();
  37. System.out.println(blipId+":"+inline);
  38. String picXml = ""
  39. + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
  40. + " <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
  41. + " <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
  42. + " <pic:nvPicPr>" + " <pic:cNvPr id=\""
  43. + id
  44. + "\" name=\"Generated\"/>"
  45. + " <pic:cNvPicPr/>"
  46. + " </pic:nvPicPr>"
  47. + " <pic:blipFill>"
  48. + " <a:blip r:embed=\""
  49. + blipId
  50. + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
  51. + " <a:stretch>"
  52. + " <a:fillRect/>"
  53. + " </a:stretch>"
  54. + " </pic:blipFill>"
  55. + " <pic:spPr>"
  56. + " <a:xfrm>"
  57. + " <a:off x=\"0\" y=\"0\"/>"
  58. + " <a:ext cx=\""
  59. + width
  60. + "\" cy=\""
  61. + height
  62. + "\"/>"
  63. + " </a:xfrm>"
  64. + " <a:prstGeom prst=\"rect\">"
  65. + " <a:avLst/>"
  66. + " </a:prstGeom>"
  67. + " </pic:spPr>"
  68. + " </pic:pic>"
  69. + " </a:graphicData>" + "</a:graphic>";
  70. inline.addNewGraphic().addNewGraphicData();
  71. XmlToken xmlToken = null;
  72. try {
  73. xmlToken = XmlToken.Factory.parse(picXml);
  74. } catch (XmlException xe) {
  75. xe.printStackTrace();
  76. }
  77. inline.set(xmlToken);
  78. inline.setDistT(0);
  79. inline.setDistB(0);
  80. inline.setDistL(0);
  81. inline.setDistR(0);
  82. CTPositiveSize2D extent = inline.addNewExtent();
  83. extent.setCx(width);
  84. extent.setCy(height);
  85. CTNonVisualDrawingProps docPr = inline.addNewDocPr();
  86. docPr.setId(id);
  87. docPr.setName("图片" + id);
  88. docPr.setDescr("测试");
  89. }
  90. }