Java Poi Word2007 占位符替换【段落,表格】

鄙人第一次在博客园展示自己写的代码,虽然代码需要优化的点很多,但这是宝贵的第一次,以后会上传更高质量的以及可以随时用的。

  1 import java.io.IOException;
  2 import java.math.BigInteger;
  3 import java.util.ArrayList;
  4 import java.util.HashMap;
  5 import java.util.Iterator;
  6 import java.util.List;
  7 import java.util.Map;
  8 import java.util.regex.Matcher;
  9 import java.util.regex.Pattern;
 10 
 11 import org.apache.poi.POIXMLDocument;
 12 import org.apache.poi.openxml4j.opc.OPCPackage;
 13 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 14 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
 15 import org.apache.poi.xwpf.usermodel.XWPFRun;
 16 import org.apache.poi.xwpf.usermodel.XWPFTable;
 17 import org.apache.poi.xwpf.usermodel.XWPFTableCell;
 18 import org.apache.poi.xwpf.usermodel.XWPFTableRow;
 19 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
 20 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
 21 import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
 22 
 23 /**
 24  * 
 25  * @author unknown
 26  * @Date 2020年3月2日
 27  * @Time 下午1:41:31
 28  */
 29 public class Word {
 30     /**
 31      * 占位符初始依赖
 32      */
 33     public static final String Symbol="$";
 34     /**
 35      * 验证主体
 36      */
 37     public static final Pattern findBody=Pattern.compile("(\\?\\{)([a-zA-Z0-9]+)(})".replace("?", Symbol));
 38     /**
 39      * 验证附属主体-后半段
 40      */
 41     public static final Pattern surplus=Pattern.compile("(\\{[a-zA-Z0-9]+}|[a-zA-Z0-9]+})");
 42     /**
 43      * 文档主体-占位符确定后的截取行为依赖
 44      */
 45     public static final Pattern keyInfo=Pattern.compile("[a-zA-Z0-9]+");
 46     
 47     /**
 48      * 
 49      * @author unknown
 50      * @Date 2020年3月2日
 51      * @Time 下午1:42:17
 52      * @param fileName 模板数据
 53      * @param map 数据格式为Map<String,String>
 54      * @param empty 查找不到数据时用的占位
 55      * @param ignorecase 忽略大小写
 56      * @return
 57      * @throws IOException
 58      */
 59     public static final XWPFDocument searchAndReplace(String fileName,Map<String, Object> map, String empty,boolean ignorecase) throws IOException {
 60         return searchAndReplace(POIXMLDocument.openPackage(fileName),map,empty,ignorecase);
 61     }
 62     /**
 63      * poi 查找word中占位符并替换
 64      * @author unknown
 65      * @Date 2020年3月2日
 66      * @Time 下午1:41:40
 67      * @param oPCPackage 模板数据
 68      * @param map 数据格式为Map<String,String>
 69      * @param empty 查找不到数据时用的占位
 70      * @param ignorecase 忽略大小写
 71      * @return
 72      * @throws IOException
 73      */
 74     public static final XWPFDocument searchAndReplace(OPCPackage oPCPackage,Map<String, Object> map, String empty,boolean ignorecase) throws IOException {
 75         return searchAndReplace(new XWPFDocument(oPCPackage),map,empty,ignorecase);
 76     }
 77     /**
 78      * poi 查找word中占位符并替换
 79      * @author unknown
 80      * @Date 2020年2月28日
 81      * @Time 下午1:59:19
 82      * @param Document 模板数据
 83      * @param map 数据格式为Map<String,String>
 84      * @param empty 查找不到数据时用的占位
 85      * @param ignorecase 忽略大小写
 86      * @return XWPFDocument
 87      * @throws IOException
 88      */
 89     public static final XWPFDocument searchAndReplace(XWPFDocument Document,Map<String, Object> map, String empty,boolean ignorecase) throws IOException {
 90         //对单行中存在多个的进行再次捕捉,表格自己优化,暂时没有贴别的解决方案
 91         //处理大小写,使其忽略大小写,精度可能降低,全部转小写
 92         if(ignorecase){
 93             //找到所有的key
 94             Map<String, Object> TmpMap=new HashMap<String,Object>();
 95             //全部替换
 96             for (String key : map.keySet()) {
 97                 //重新复制并将其转为小写
 98                 TmpMap.put(key.toLowerCase(), map.get(key));
 99             }
100             //重新赋值
101             map=TmpMap;
102         }
103         Word.ParagraphSearchAndReplace(Document.getParagraphsIterator(),map,empty,ignorecase);
104         Word.TableSearchAndReplace(Document.getTablesIterator(), map, empty, ignorecase);
105         return Document;
106     }
107     /**
108      * poi 查找word表格中占位符并替换
109      * @author unknown
110      * @Date 2020年3月2日
111      * @Time 下午2:01:47
112      * @param itTable
113      * @param map
114      * @param empty
115      * @param ignorecase
116      */
117     public static final void TableSearchAndReplace(Iterator<XWPFTable> itTable,Map<String, Object> map, String empty,boolean ignorecase) {
118         while (itTable.hasNext()) {
119             XWPFTable table = itTable.next();
120             int count = table.getNumberOfRows();
121             for (int i = 0; i < count; i++) {
122                 XWPFTableRow row = table.getRow(i);
123                 List<XWPFTableCell> cells = row.getTableCells();
124                 for (XWPFTableCell cell : cells) {
125                     List<XWPFParagraph> Paragraph= cell.getParagraphs();
126                     if(Paragraph.size()>1) {
127                         Word.ParagraphSearchAndReplace(Paragraph,map,empty,ignorecase);
128                     }else {
129                         String nowText=cell.getText();
130                         // 第一次查询
131                         Matcher DivFind = findBody.matcher(nowText);
132                         
133                         while(DivFind.find()){
134                             String key=new StringBuffer(nowText).substring(DivFind.start(2),DivFind.end(2)).toString();
135                             // 忽略大小写
136                             if(ignorecase){
137                                 //全部置为小写
138                                 key=key.toLowerCase();
139                             }
140                             //删除表格内容(改到代码内部,不进行数据替换时不删除)
141                             //cell.removeParagraph(0);
142 //                            System.out.println(key);
143 //                            if(key.contains("sampledate")) {
144 //                                System.out.println("");
145 //                            }
146                             if(key!=null && map.containsKey(key)){
147                                 //获取数据
148                                 Object _value= map.get(key);
149                                 
150                                 String ClassName= _value.getClass().getName().toLowerCase();
151                                 //System.out.println(ClassName);
152                                 if(ClassName.equals("java.lang.string")) {
153                                     //数据转String
154                                     String value=_value!=null?_value.toString():empty;
155                                     //直接替换占位符开始的部位
156                                     nowText=new StringBuffer(nowText).replace(DivFind.start(), DivFind.end(), value).toString();
157                                     
158                                     if(!DivFind.find()) {
159                                         //删除表格内容
160                                         cell.removeParagraph(0);
161                                         //新建段落
162                                         XWPFParagraph pIO = cell.addParagraph();
163                                         //新建字体开始
164                                         XWPFRun rIO = pIO.createRun();
165                                         rIO.setBold(false);
166                                         rIO.setFontFamily("仿宋_GB2312");
167                                         rIO.setFontSize(11);
168                                         rIO.setText(nowText);
169                                     }else {
170                                         DivFind=findBody.matcher(nowText);
171                                     }
172                                     
173                                     
174                                 }else if(ClassName.equals("com.jeesite.modules.utils.word$wordtable")) {
175                                     //删除表格内容
176                                     cell.removeParagraph(0);
177                                     
178                                     WordTable tableData=(WordTable)_value;
179                                     XWPFParagraph cellPara = cell.addParagraph();
180                                     XWPFTable cell_table= cell.insertNewTbl(cellPara.getCTP().newCursor());
181                                     Word.createTable(cell_table, tableData.data, tableData.head,tableData.width,false);
182                                 }
183                                 
184                             }else{
185                                 nowText=new StringBuffer(nowText).replace(DivFind.start(), DivFind.end(), empty).toString();
186                                 //删除表格内容
187                                 cell.removeParagraph(0);
188                                 //新建段落
189                                 XWPFParagraph pIO = cell.addParagraph();
190                                 //新建字体开始
191                                 XWPFRun rIO = pIO.createRun();
192                                 rIO.setBold(false);
193                                 rIO.setFontFamily("仿宋_GB2312");
194                                 rIO.setFontSize(11);
195                                 rIO.setText(nowText);
196                             }
197                         }
198                     }
199                 }
200             }
201         }
202     }
203     
204     /**
205      * poi 查找word表格中占位符并替换
206      * @author unknown
207      * @Date 2020年3月2日
208      * @Time 下午2:39:10
209      * @param tables
210      * @param map
211      * @param empty
212      * @param ignorecase
213      */
214     public static void TableSearchAndReplace(List<XWPFTable> tables, Map<String, Object> map, String empty,boolean ignorecase) {
215         Word.TableSearchAndReplace(tables.iterator(), map, empty, ignorecase);
216     }
217     /**
218      * poi 查找word段落中占位符并替换
219      * @author unknown
220      * @Date 2020年3月2日
221      * @Time 下午2:02:18
222      * @param itPara
223      * @param map
224      * @param empty
225      * @param ignorecase
226      */
227     public static final void ParagraphSearchAndReplace(Iterator<XWPFParagraph> itPara,Map<String, Object> map, String empty,boolean ignorecase) {
228         //循环文本主体,文本支值循环一遍
229         while (itPara.hasNext()) {
230             // 获取一行文本结构主体
231             XWPFParagraph tmpBody = itPara.next();
232             // 获取一行的文本结构数组
233             List<XWPFRun> run = tmpBody.getRuns();
234             // 循环文本(每一次循环确定一个占位符)
235             for (int runIndex = 0; runIndex < run.size(); runIndex++) {
236                 // 获取到占位符文本深度(即文本从左到右的非可用长度),避免处理过多的文本
237                 int runDepth=0;
238                 //记录总共深度(即在for里边用了几个runIndex)
239                 int j=0;
240                 //最终确认的文本位置
241                 int findIndex=runIndex;
242                 //数据最终的key
243                 String DivText=null;
244                 //获取文本节点的第一个
245                 String NowRunText = run.get(runIndex).getText(run.get(runIndex).getTextPosition());
246                 if(NowRunText==null){
247                     continue;
248                 }
249                 //第一次查找
250                 Matcher DivFind = findBody.matcher(NowRunText);
251                 //全文本节点
252                 String AllRunText = NowRunText;
253                 //查找到符号位置
254                 if(NowRunText.contains(Symbol)){
255                     //
256                     j=runIndex;
257                     //一直循环知道处理完成,直到所有文本全部找到
258                     while(!DivFind.find()){
259                         //继续深度处理,记录处理深度
260                         runDepth++;
261                         j++;
262                         //当前文本
263                         String NewRunText=run.get(j).getText(run.get(j).getTextPosition());
264                         //存在文本
265                         if(NewRunText!=null){
266                             //拼接全部文本
267                             AllRunText+=NewRunText;
268                         }
269                         //查找到符号位置(原位优化程序用的,但是在处理上存在问题所以注释掉)
270                         //if(NewRunText.contains(Symbol)){
271                             //重置第一个确认字
272                             //NowRunText=NewRunText;
273                             //重置第一个确认位
274                             //findIndex=runDepth;
275                         //}
276                         //继续深度获取文本
277                         DivFind=findBody.matcher(AllRunText);
278                     }
279                     //重置查找,避免过多运行find找不到参数
280                     DivFind=findBody.matcher(AllRunText);
281                     //只处理占位符位置,可能存在其他文本,所以使用find
282                     if(DivFind.find()){//之查找一个多余的不动
283                         //直接拉取字符
284                         DivText=new StringBuffer(AllRunText).substring(DivFind.start(2),DivFind.end(2)).toString();
285                         // 忽略大小写
286                         if(ignorecase){
287                             //全部置为小写
288                             DivText=DivText.toLowerCase();
289                         }
290 //                        if(DivText.contains("sampledate")) {
291 //                            System.out.println("");
292 //                        }
293                         //判断是否存在文本,是否存在数据
294                         if(DivText!=null && map.containsKey(DivText)){
295                             //获取数据
296                             Object _value= map.get(DivText);
297                             //数据转String
298                             String value=_value!=null?_value.toString():empty;
299                             //直接替换占位符开始的部位
300                             String rText=new StringBuffer(NowRunText).replace(DivFind.start(), DivFind.end(), value).toString();
301                             //对单行中存在多个的进行再次捕捉,正则
302                             Matcher mg = findBody.matcher(rText);
303                             //查找新的占位符
304                             while(mg.find()){
305                                 //查找新的key
306                                 String newKey=new StringBuffer(rText).substring(mg.start(2), mg.end(2)).toString();
307                                 //忽略大小写
308                                 if(ignorecase){
309                                     //全部置为小写
310                                     newKey=newKey.toLowerCase();
311                                 }
312                                 //查找新的数据
313                                 if(newKey!=null && map.containsKey(newKey)){
314                                     //获取新的数据
315                                     Object _value1= map.get(newKey);
316                                     //数据转String
317                                     String value1=_value1!=null?_value1.toString():empty;
318                                     //覆盖赋值
319                                     rText=new StringBuffer(rText).replace(mg.start(), mg.end(), value1).toString();
320                                 }
321                                 //替换word文本
322                                 mg = findBody.matcher(rText);
323                             }
324                             //将文本置换
325                             run.get(findIndex).setText(rText,0);
326                         }else{
327                             //查找不到为空
328                             run.get(findIndex).setText(empty,0);
329                         }
330                         //清空剩余项(对深度处理的数据进行清理)
331                         int g=runIndex;
332                         //对深度处理的数据进行清除占位符
333                         for (int i = 0; i < runDepth; i++) {
334                             g++;
335                             //获取要清理的文本
336                             String ClearText =run.get(g).getText(run.get(g).getTextPosition());
337                             //获取要清理的正则规则
338                             Matcher ClearFind=surplus.matcher(ClearText);
339                             Matcher ClearKey=keyInfo.matcher(ClearText);
340                             //寻找与规则相同的文本
341                             if(ClearFind.find()){
342                                 //清空规则内的信息(只清除第一个存在的规则文本)
343                                 ClearText=new StringBuffer(ClearText).replace(ClearFind.start(),ClearFind.end(), "").toString();
344                             }
345                             //完整规则,不进行查找,确保值只是英文以及数字
346                             else if(ClearKey.matches()){
347                                 ClearText=new StringBuffer(ClearText).replace(ClearKey.start(),ClearKey.end(), "").toString();
348                             }
349                             //不存在的直接删除文本开始的第一个字符
350                             else{
351                                 ClearText=ClearText.substring(1);
352                             }
353                             //重新赋值
354                             run.get(g).setText(ClearText,0);
355                             
356                             //如果文本中存在占位符头,将深度减去1在次使用
357                             if(ClearText.contains(Symbol)){
358                                 j--;
359                             }
360                         }
361                         //跳过已经使用的深度循环
362                         runIndex=j;
363                     }
364                 }
365             }
366         }
367     }
368     /**
369      * 
370      * @author unknown
371      * @Date 2020年3月2日
372      * @Time 下午3:51:17
373      * @param paragraph
374      * @param map
375      * @param empty
376      * @param ignorecase
377      */
378     public static void ParagraphSearchAndReplace(List<XWPFParagraph> paragraph, Map<String, Object> map, String empty,boolean ignorecase) {
379         Word.ParagraphSearchAndReplace(paragraph.iterator(),map,empty,ignorecase);
380     }
381     /**
382      * 创建简单表格
383      * @author unknown
384      * @Date 2020年2月28日
385      * @Time 下午4:44:03
386      * @param obj 单元格或者文档
387      * @param table 表格数据
388      * @param head 头
389      * @return
390      */
391     public static final void createTable(XWPFTable XWPtable,List<Map<String,Object>> table,Map<String,String> head,List<Integer> width,boolean isFound) {
392         //
393         Iterator<String> tmp_head=table.get(0).keySet().iterator();
394         //表格行数
395         int rowNum = table.size();
396         //表格列数
397         int colNum = table.get(0).keySet().size();
398         //表格占用行数
399         int headNum = head!=null?1:0;
400         //表格数据开始行数
401         int i = headNum;
402         //表格总行数
403         rowNum += headNum;
404         if(headNum==1) {
405             int tmpcolNum=head.keySet().size();
406             colNum=colNum>=tmpcolNum?colNum:tmpcolNum;
407         }
408         
409         if(!isFound){
410             for (int j = 0; j < rowNum; j++) {
411                 XWPFTableRow row= XWPtable.insertNewTableRow(j);
412                 for (int k = 0; k < colNum; k++) {
413                     row.createCell();
414                 }
415             }
416             
417         }
418         
419         if(XWPtable!=null) {
420             //如果表格存在头
421             if(headNum==1) {
422                 //更改新的头
423                 tmp_head=head.keySet().iterator();
424                 //列计数
425                 int j=0;
426                 //循环头
427                 while(tmp_head.hasNext()) {
428                     //添加头
429                     XWPtable.getRow(0).getCell(j).setText(head.get(tmp_head.next()).toString());
430                     
431                     if(width!=null && width.size()>0) {
432                         CTTcPr tcpr =  XWPtable.getRow(0).getCell(j).getCTTc().addNewTcPr();
433                         CTTblWidth cellw = tcpr.addNewTcW();
434                         cellw.setType(STTblWidth.DXA);
435                         cellw.setW(BigInteger.valueOf(width.get(j<width.size()?j:width.size()-1)));
436                     }
437                     
438                     //列计数+1
439                     j+=1;
440                 }
441             }
442             //数据添加
443             for (;i< table.size()+headNum; i++) {
444                 tmp_head=headNum==1? head.keySet().iterator():table.get(0).keySet().iterator();
445                 //获取每行数据
446                 Map<String,Object> data=table.get(i-headNum);
447                 //列计数
448                 int j=0;
449                 //循环
450                 while(tmp_head.hasNext()) {
451                     //添加数据
452                     String key= tmp_head.next();
453                     if(data.containsKey(key)) {
454                         XWPtable.getRow(i).getCell(j).setText(data.get(key).toString());
455                     }
456                     
457                     if(width!=null && width.size()>0) {
458                         CTTcPr tcpr =  XWPtable.getRow(i).getCell(j).getCTTc().addNewTcPr();
459                         CTTblWidth cellw = tcpr.addNewTcW();
460                         cellw.setType(STTblWidth.DXA);
461                         cellw.setW(BigInteger.valueOf(width.get(j<width.size()?j:width.size()-1)));
462                     }
463                     
464                     //列计数+1
465                     j+=1;
466                 }
467             }
468         }
469     }
470     /**
471      *新的表格对象(面向数据),简单表格(目前只能在表格中使用暂时无法定位段落)
472      * @author unknown
473      * @Date 2020年3月2日
474      * @Time 上午10:40:08
475      */
476     public static class WordTable {
477         
478         private List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();
479         private Map<String,String> head=new HashMap<String, String>();
480         private List<Integer> width=new ArrayList<Integer>();
481         
482         public void addWidth(int w) {
483             this.width.add(w);
484         }
485         public void setWidth(List<Integer> w) {
486             this.width=w;
487         }
488         
489         public void setData(List<Map<String, Object>> data) {
490             this.data = data;
491         }
492 
493         public void setHead(Map<String, String> head) {
494             this.head = head;
495         }
496 
497     }
498     
499 }

对,类的使用方式进行描述

        //这里应该不用多做解释了吧,就是从dao层中查询数据
        Map<String,Object> data= dao.findWordInfoDate(id);
        //这是在上边的Word类里边的静态类,这个位置的作用就是绘画表格
        WordTable table = new WordTable();
                //定义表格的表头,当然也可以不要,默认就是数据的字段
        table.setHead(new LinkedHashMap<String, String>(){
            private static final long serialVersionUID = 1L;
            {
                this.put("sampleName", "样品名称");
                this.put("appllyNo", "原始编号");
                this.put("testProject", "项目名称");
                this.put("result", "检测结果");
                this.put("resultUnit", "结果单位");
                this.put("resultIsEligible", "结果判定");
            }
        });
        //塞入数据这里的格式为List<Map<String,Object>>
        table.setData(dao.findWordListDate(id));
        //这里设置列表的宽度,其实这里是一个List,setWidth(list),
        table.addWidth(356*5);
        table.addWidth(356*8);
        table.addWidth(356*5);
        table.addWidth(356*4+80);
        table.addWidth(356*3);
        table.addWidth(356*3);
        //把整个对象放进数据里
        data.put("listData",table);
        //打开表格并替换内容
        XWPFDocument Document=  Word.searchAndReplace(path.replace("?","Template.docx"),data, "", true);
        //定义页脚的数据
        Map<String,Object> footdata= new HashMap<String, Object>();
        
        footdata.put("nowusername", user.getUserName());
        footdata.put("nowdate", sdf.format(new Date()));
        //获取文档页脚
        List<XWPFFooter> pageFooters = Document.getFooterList();
        for (int i = 0; i < pageFooters.size(); i++) {
           //获取表格列表并替换占位符
       Word.TableSearchAndReplace(pageFooters.get(i).getTables(), footdata,"", true);
        }
                    

然后就是模板文档的样式

 再然后就是替换完的样子

 然然后就没咯。。。

因为这里的特殊放表格方式,在Word类里边的替换表格的位置有个判断需要留意一下哦~

扫描二维码关注公众号,回复: 9543885 查看本文章

我就直接把代码直接给放上了,需要理解的要自己阅读代码哦~

大概功能就是替换word里的展位字符,并扩展替换表格(目前支支持替换表格里的占位符值作为表,段落里的需要自己扩展哦)

如果觉的我写的还行,就给我点个推荐吧~推荐吧~推荐吧~荐吧~吧~啊



猜你喜欢

转载自www.cnblogs.com/UnknownRegister/p/12400625.html