項目系統中打印功能,導出 word 文檔功能是挺常用的,本文介紹自定文檔模板,程序實現模板內容中值替代的功能。
模板文件 template.docx

執行 main
public static void main(String[] args) { //模板文檔路徑 String filePath = "D:/DOC/template.docx"; String res = String.valueOf(new Date().getTime()); //生成文檔路徑 String outFile = "D:/DOC/插入值后文檔" + res + ".docx"; try { GeneralTemplateTool gtt = new GeneralTemplateTool(); Map<String, Object> params = new HashMap<String, Object>(); //創建替代模板里段落中如${title}值開始 params.put("title","標題文字 by Stephen" ); params.put("Tab1Title","表一"); params.put("Tab2Title","表二"); //......對應模板擴展 //創建替代模板里段落中如${title}值結束 //創建替代&生成模板里tab1標識的表格中的值開始 List<Map<String,String>> tab1list = new ArrayList<Map<String,String>>(); for (int i = 1; i <= 3; i++) { Map<String, String> map = new HashMap<String, String>(); map.put("name", "張" + i); map.put("age", "1" + i); map.put("sex", "男"); map.put("job", "職業"+i); map.put("hobby", "愛好"+i); map.put("phone", "1312365322"+i); tab1list.add(map); } params.put("tab1", tab1list); //創建替代&生成模板里tab1標識的表格中的值結束 //創建替代&生成模板里tab2標識的表格中的值開始 List<Map<String,String>> tab2list = new ArrayList<Map<String,String>>(); for (int i = 1; i <= 3; i++) { Map<String, String> map = new HashMap<String, String>(); map.put("name", "王" + i); map.put("age", "1" + i); map.put("sex", "女"); map.put("job", "職業"+i); tab2list.add(map); } params.put("tab2", tab2list); //創建替代&生成模板里tab2標識的表格中的值結束 //創建替代&生成模板里tab3標識的表格中的值開始 List<Map<String,String>> tab3list = new ArrayList<Map<String,String>>(); for (int i = 1; i <= 4; i++) { Map<String, String> map = new HashMap<String, String>(); map.put("a", "a列值" + i); map.put("b", "b列值" + i); map.put("c", "c列值" + i); tab3list.add(map); } params.put("tab3", tab3list); //創建替代&生成模板里tab3標識的表格中的值結束 //......對應模板擴展 gtt.templateWrite(filePath, outFile, params); System.out.println("生成模板成功"); System.out.println(outFile); } catch (Exception e) { System.out.println("生成模板失敗"); e.printStackTrace(); } }
后台打印日志
-------解析出第 1 個表------開始處理 ###表標識值:tab1 ----${tab1} ----姓名 第1列:${name} 第2列:${age} 第3列:${sex} 第4列:${job} 第5列:${hobby} 第6列:${phone} 開始復制第0行 ${name} 第1列:張1 ${age} 第2列:11 ${sex} 第3列:男 ${job} 第4列:職業1 ${hobby} 第5列:愛好1 ${phone} 第6列:13123653221 開始復制第1行 ${name} 第1列:張2 ${age} 第2列:12 ${sex} 第3列:男 ${job} 第4列:職業2 ${hobby} 第5列:愛好2 ${phone} 第6列:13123653222 開始復制第2行 ${name} 第1列:張3 ${age} 第2列:13 ${sex} 第3列:男 ${job} 第4列:職業3 ${hobby} 第5列:愛好3 ${phone} 第6列:13123653223 -------解析出第 1 個表------結束處理 -------解析出第 2 個表------開始處理 ###表標識值:tab2 ----${tab2} ----姓名 第1列:${name} 第2列:${age} 第3列:${sex} 第4列:${job} 開始復制第0行 ${name} 第1列:王1 ${age} 第2列:11 ${sex} 第3列:女 ${job} 第4列:職業1 開始復制第1行 ${name} 第1列:王2 ${age} 第2列:12 ${sex} 第3列:女 ${job} 第4列:職業2 開始復制第2行 ${name} 第1列:王3 ${age} 第2列:13 ${sex} 第3列:女 ${job} 第4列:職業3 -------解析出第 2 個表------結束處理 -------解析出第 3 個表------開始處理 ###表標識值:tab3 ----${tab3} ----a ----列 第1列:${a} 第2列:${b} 第3列:${c} 開始復制第0行 ${a} 第1列:a列值1 ${b} 第2列:b列值1 ${c} 第3列:c列值1 開始復制第1行 ${a} 第1列:a列值2 ${b} 第2列:b列值2 ${c} 第3列:c列值2 開始復制第2行 ${a} 第1列:a列值3 ${b} 第2列:b列值3 ${c} 第3列:c列值3 開始復制第3行 ${a} 第1列:a列值4 ${b} 第2列:b列值4 ${c} 第3列:c列值4 -------解析出第 3 個表------結束處理 生成模板成功 D:/DOC/插入值后文檔1558313967002.docx
程序執行完生成新的文檔

查看效果:值被動態加入,格式保留。

具體實現代碼:
/** * 用一個docx文檔作為模板,程序替換其中的內容,再寫入目標文檔中。 * @param filePath * @param outFile * @param params * @throws Exception */ public void templateWrite(String filePath, String outFile, Map<String, Object> params) throws Exception { InputStream is = new FileInputStream(filePath); //System.out.println(filePath); XWPFDocument doc = new XWPFDocument(is); // 替換段落里面的變量 this.replaceInPara(doc, params); // 替換多個表格里面的變量並插入數據 this.insertValueToTables(doc, params); OutputStream os = new FileOutputStream(outFile); doc.write(os); this.close(os); this.close(is); } /** * 替換段落里面的變量 * * @param doc * 要替換的文檔 * @param params * 參數 */ private void replaceInPara(XWPFDocument doc, Map<String, Object> params) { Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator(); XWPFParagraph para; while (iterator.hasNext()) { para = iterator.next(); this.replaceInPara(para, params); } } /** * 替換段落里面的變量 * * @param para * 要替換的段落 * @param params * 參數 */ private boolean replaceInPara(XWPFParagraph para, Map<String, Object> params) { boolean data = false; List<XWPFRun> runs; Matcher matcher; if (this.matcher(para.getParagraphText()).find()) { runs = para.getRuns(); for (int i = 0; i < runs.size(); i++) { XWPFRun run = runs.get(i); String runText = run.toString(); matcher = this.matcher(runText); if (matcher.find()) { while ((matcher = this.matcher(runText)).find()) { String str=String.valueOf(params.get(matcher.group(1))); //System.out.println("----"+runText); //System.out.println("----"+str); runText = matcher.replaceFirst(str); } Boolean isBold = run.isBold(); Boolean isItalic = run.isItalic(); Boolean isStrike = run.isStrike(); UnderlinePatterns Underline = run.getUnderline(); String Color = run.getColor(); int TextPosition = run.getTextPosition(); int FontSize = run.getFontSize(); String FontFamily = run.getFontFamily(); CTR ctr =run.getCTR(); para.removeRun(i); //para.insertNewRun(i).setText(runText); XWPFRun newrun = para.insertNewRun(i); newrun.setText(runText); try { // 復制格式 newrun.setBold(isBold); newrun.setItalic(isItalic); newrun.setStrike(isStrike); newrun.setUnderline(Underline); newrun.setColor(Color); newrun.setTextPosition(TextPosition); if (FontSize != -1) { newrun.setFontSize(FontSize); CTRPr rpr = newrun.getCTR().isSetRPr() ? newrun.getCTR().getRPr() : newrun.getCTR().addNewRPr(); CTFonts fonts = rpr.isSetRFonts() ? rpr.getRFonts() : rpr.addNewRFonts(); fonts.setAscii(FontFamily); fonts.setEastAsia(FontFamily); fonts.setHAnsi(FontFamily); } if (FontFamily != null) { newrun.setFontFamily(FontFamily); } if (ctr != null) { Boolean flat = false; try { flat = ctr.isSetRPr(); } catch (Exception e) { } if (flat) { CTRPr tmpRPr = ctr.getRPr(); if (tmpRPr.isSetRFonts()) { CTFonts tmpFonts = tmpRPr.getRFonts(); CTRPr cellRPr = newrun.getCTR().isSetRPr() ? newrun .getCTR().getRPr() : newrun .getCTR().addNewRPr(); CTFonts cellFonts = cellRPr.isSetRFonts() ? cellRPr .getRFonts() : cellRPr .addNewRFonts(); cellFonts.setAscii(tmpFonts.getAscii()); cellFonts.setAsciiTheme(tmpFonts .getAsciiTheme()); cellFonts.setCs(tmpFonts.getCs()); cellFonts.setCstheme(tmpFonts.getCstheme()); cellFonts.setEastAsia(tmpFonts .getEastAsia()); cellFonts.setEastAsiaTheme(tmpFonts .getEastAsiaTheme()); cellFonts.setHAnsi(tmpFonts.getHAnsi()); cellFonts.setHAnsiTheme(tmpFonts .getHAnsiTheme()); } } } } catch (Exception e) { e.printStackTrace(); } } } data = true; } else if (this.matcherRow(para.getParagraphText())) { runs = para.getRuns(); // System.out.println("run " + runs); data = true; } return data; } /** * 按模版行樣式填充數據,暫未實現特殊樣式填充(如列合並),只能用於普通樣式(如段落間距 縮進 字體 對齊) * @param doc * 要替換的文檔 * @param params * 參數 * @throws Exception */ private void insertValueToTables(XWPFDocument doc, Map<String, Object> params) throws Exception { Iterator<XWPFTable> iterator = doc.getTablesIterator(); XWPFTable table = null; int z =1; while (iterator.hasNext()) { //行 List<XWPFTableRow> rows = null; //列 List<XWPFTableCell> cells = null; List<XWPFParagraph> paras; table = iterator.next(); System.out.println("-------解析出第 "+z+" 個表------開始處理"); //獲取表格行數據list rows = table.getRows(); XWPFTableRow tmpRow = null; //第二行為模板行 tmpRow = rows.get(1); //模版列 List<XWPFTableCell> tmpCells = null; XWPFTableCell tmpCell = null; tmpCells = tmpRow.getTableCells(); List<Map> tablist =null; List<String> listkey = new ArrayList<String>(); for (int i = 1; i <= rows.size(); i++) { cells = rows.get(i - 1).getTableCells(); //獲取當前行所有列 if(i==1){ int intcell = 1; //遍歷列 for (XWPFTableCell cell : cells) { if (intcell == 1) { //取第一行第一列表標識並替代${ tab1} 姓名 值 為姓名 map里取對應表list數據 String flagtemp = cell.getText(); flagtemp = flagtemp.substring(flagtemp.indexOf("{")+1, flagtemp.lastIndexOf("}")); System.out.println("###表標識值:" +flagtemp); tablist = (List<Map>) params.get(flagtemp); paras = cell.getParagraphs(); for (XWPFParagraph para : paras) { List<XWPFRun> runs; runs = para.getRuns(); for (int m = 0; m < runs.size(); m++) { XWPFRun run = runs.get(m); String runText = run.toString(); System.out.println("----"+runText); Matcher matcher; matcher = this.matcher(runText); if (matcher.find()) { while ((matcher = this.matcher(runText)).find()) { runText = matcher.replaceFirst(""); } para.removeRun(m); para.insertNewRun(m).setText(runText); } } } } intcell++; break; } }else if(i==2){ //第二行替代值並創建list key用 int intcell = 1; for (XWPFTableCell cell : cells) { System.out.println("第"+intcell + "列:" + cell.getText()); //Map mapth = tablist.get(0); paras = cell.getParagraphs(); for (XWPFParagraph para : paras) { //讀取的值去掉${} String keystr = para.getParagraphText(); keystr = keystr.substring(keystr.indexOf("{")+1, keystr.lastIndexOf("}")); listkey.add(keystr); } intcell++; } } } //開始動態創建表 for (int i = 0; i < tablist.size(); i++) { System.out.println("開始復制第" + i + "行"); XWPFTableRow row = table.createRow(); row.setHeight(tmpRow.getHeight()); Map<String,String> tempmap = tablist.get(i); cells = row.getTableCells(); // 插入的行會填充與表格第一行相同的列數 for (int k = 0 ; k < cells.size(); k++) { tmpCell = tmpCells.get(k); XWPFTableCell cell = cells.get(k); setCellText(tmpCell, cell, tablist.get(i).get(listkey.get(k)).toString()); System.out.println("第"+(k+1)+"列:" +tablist.get(i).get(listkey.get(k)).toString()); } // 繼續寫剩余的列 for (int j = cells.size(); j < listkey.size(); j++) { tmpCell = tmpCells.get(j); XWPFTableCell cell = row.addNewTableCell(); setCellText(tmpCell, cell, tablist.get(i).get(listkey.get(j)).toString()); System.out.println("第"+(j+1)+"列:" +tablist.get(i).get(listkey.get(j)).toString()); } } // 刪除表標識行 table.removeRow(1); System.out.println("-------解析出第 "+z+" 個表------結束處理"); z++; } } public void setCellText(XWPFTableCell tmpCell, XWPFTableCell cell, String text) throws Exception { CTTc cttc2 = tmpCell.getCTTc(); CTTcPr ctPr2 = cttc2.getTcPr(); CTTc cttc = cell.getCTTc(); CTTcPr ctPr = cttc.addNewTcPr(); cell.setColor(tmpCell.getColor()); // cell.setVerticalAlignment(tmpCell.getVerticalAlignment()); if (ctPr2.getTcW() != null) { ctPr.addNewTcW().setW(ctPr2.getTcW().getW()); } if (ctPr2.getVAlign() != null) { ctPr.addNewVAlign().setVal(ctPr2.getVAlign().getVal()); } if (cttc2.getPList().size() > 0) { CTP ctp = cttc2.getPList().get(0); if (ctp.getPPr() != null) { if (ctp.getPPr().getJc() != null) { cttc.getPList().get(0).addNewPPr().addNewJc() .setVal(ctp.getPPr().getJc().getVal()); } } } if (ctPr2.getTcBorders() != null) { ctPr.setTcBorders(ctPr2.getTcBorders()); } XWPFParagraph tmpP = tmpCell.getParagraphs().get(0); XWPFParagraph cellP = cell.getParagraphs().get(0); XWPFRun tmpR = null; if (tmpP.getRuns() != null && tmpP.getRuns().size() > 0) { tmpR = tmpP.getRuns().get(0); String runText = tmpR.toString(); System.out.println(runText); } XWPFRun cellR = cellP.createRun(); cellR.setText(text); // 復制字體信息 if (tmpR != null) { cellR.setBold(tmpR.isBold()); cellR.setItalic(tmpR.isItalic()); cellR.setStrike(tmpR.isStrike()); cellR.setUnderline(tmpR.getUnderline()); cellR.setColor(tmpR.getColor()); cellR.setTextPosition(tmpR.getTextPosition()); if (tmpR.getFontSize() != -1) { cellR.setFontSize(tmpR.getFontSize()); } if (tmpR.getFontFamily() != null) { cellR.setFontFamily(tmpR.getFontFamily()); } if (tmpR.getCTR() != null) { if (tmpR.getCTR().isSetRPr()) { CTRPr tmpRPr = tmpR.getCTR().getRPr(); if (tmpRPr.isSetRFonts()) { CTFonts tmpFonts = tmpRPr.getRFonts(); CTRPr cellRPr = cellR.getCTR().isSetRPr() ? cellR .getCTR().getRPr() : cellR.getCTR().addNewRPr(); CTFonts cellFonts = cellRPr.isSetRFonts() ? cellRPr .getRFonts() : cellRPr.addNewRFonts(); cellFonts.setAscii(tmpFonts.getAscii()); cellFonts.setAsciiTheme(tmpFonts.getAsciiTheme()); cellFonts.setCs(tmpFonts.getCs()); cellFonts.setCstheme(tmpFonts.getCstheme()); cellFonts.setEastAsia(tmpFonts.getEastAsia()); cellFonts.setEastAsiaTheme(tmpFonts.getEastAsiaTheme()); cellFonts.setHAnsi(tmpFonts.getHAnsi()); cellFonts.setHAnsiTheme(tmpFonts.getHAnsiTheme()); } } } } // 復制段落信息 cellP.setAlignment(tmpP.getAlignment()); cellP.setVerticalAlignment(tmpP.getVerticalAlignment()); cellP.setBorderBetween(tmpP.getBorderBetween()); cellP.setBorderBottom(tmpP.getBorderBottom()); cellP.setBorderLeft(tmpP.getBorderLeft()); cellP.setBorderRight(tmpP.getBorderRight()); cellP.setBorderTop(tmpP.getBorderTop()); cellP.setPageBreak(tmpP.isPageBreak()); if (tmpP.getCTP() != null) { if (tmpP.getCTP().getPPr() != null) { CTPPr tmpPPr = tmpP.getCTP().getPPr(); CTPPr cellPPr = cellP.getCTP().getPPr() != null ? cellP .getCTP().getPPr() : cellP.getCTP().addNewPPr(); // 復制段落間距信息 CTSpacing tmpSpacing = tmpPPr.getSpacing(); if (tmpSpacing != null) { CTSpacing cellSpacing = cellPPr.getSpacing() != null ? cellPPr .getSpacing() : cellPPr.addNewSpacing(); if (tmpSpacing.getAfter() != null) { cellSpacing.setAfter(tmpSpacing.getAfter()); } if (tmpSpacing.getAfterAutospacing() != null) { cellSpacing.setAfterAutospacing(tmpSpacing .getAfterAutospacing()); } if (tmpSpacing.getAfterLines() != null) { cellSpacing.setAfterLines(tmpSpacing.getAfterLines()); } if (tmpSpacing.getBefore() != null) { cellSpacing.setBefore(tmpSpacing.getBefore()); } if (tmpSpacing.getBeforeAutospacing() != null) { cellSpacing.setBeforeAutospacing(tmpSpacing .getBeforeAutospacing()); } if (tmpSpacing.getBeforeLines() != null) { cellSpacing.setBeforeLines(tmpSpacing.getBeforeLines()); } if (tmpSpacing.getLine() != null) { cellSpacing.setLine(tmpSpacing.getLine()); } if (tmpSpacing.getLineRule() != null) { cellSpacing.setLineRule(tmpSpacing.getLineRule()); } } // 復制段落縮進信息 CTInd tmpInd = tmpPPr.getInd(); if (tmpInd != null) { CTInd cellInd = cellPPr.getInd() != null ? cellPPr.getInd() : cellPPr.addNewInd(); if (tmpInd.getFirstLine() != null) { cellInd.setFirstLine(tmpInd.getFirstLine()); } if (tmpInd.getFirstLineChars() != null) { cellInd.setFirstLineChars(tmpInd.getFirstLineChars()); } if (tmpInd.getHanging() != null) { cellInd.setHanging(tmpInd.getHanging()); } if (tmpInd.getHangingChars() != null) { cellInd.setHangingChars(tmpInd.getHangingChars()); } if (tmpInd.getLeft() != null) { cellInd.setLeft(tmpInd.getLeft()); } if (tmpInd.getLeftChars() != null) { cellInd.setLeftChars(tmpInd.getLeftChars()); } if (tmpInd.getRight() != null) { cellInd.setRight(tmpInd.getRight()); } if (tmpInd.getRightChars() != null) { cellInd.setRightChars(tmpInd.getRightChars()); } } } } } /** * 正則匹配字符串 * * @param str * @return */ private Matcher matcher(String str) { Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); return matcher; } /** * 正則匹配字符串 * * @param str * @return */ private boolean matcherRow(String str) { Pattern pattern = Pattern.compile("\\$\\[(.+?)\\]", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); return matcher.find(); } /** * 關閉輸入流 * * @param is */ private void close(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 關閉輸出流 * * @param os */ private void close(OutputStream os) { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } }
相關 jar 的引入
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.xwpf.usermodel.Document; import org.apache.poi.xwpf.usermodel.UnderlinePatterns; 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; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTInd; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSpacing; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
注意:
有個細節,模板文檔中所有諸如:${title} 元素都得替換自身一次,才能被程序識別為一個整體元素被動態替換掉值!
操作如下圖:

歡迎訂閱 Stephen 公眾號

