鄙人第一次在博客园展示自己写的代码,虽然代码需要优化的点很多,但这是宝贵的第一次,以后会上传更高质量的以及可以随时用的。
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里的展位字符,并扩展替换表格(目前支支持替换表格里的占位符值作为表,段落里的需要自己扩展哦)
如果觉的我写的还行,就给我点个推荐吧~推荐吧~推荐吧~荐吧~吧~啊