Java Poi Word模板內容替換【段落,表格】


 

鄙人第一次在博客園展示自己寫的代碼,雖然代碼需要優化的點很多,但這是寶貴的第一次,以后會上傳更高質量的以及可以隨時用的。

  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類里邊的替換表格的位置有個判斷需要留意一下哦~

我就直接把代碼直接給放上了,需要理解的要自己閱讀代碼哦~

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

如果覺的我寫的還行,就給我點個推薦吧~推薦吧~推薦吧~薦吧~吧~啊




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM