POI產生WORD文檔,poiword文檔
POI產生WORD文檔
POI為Java系處理office文檔的比較優秀的開源庫,其中對於Excel的處理最為優秀,文檔也寫的很詳細。不過很多網友都認為它在word文檔處理方面就遜色很多,不過對於我本次的完成文檔的產生我依然選擇了POI。
需要完成功能配置word模版
採用${xx}方式配置標籤,如果是表格在對應一行一列配置表格名稱
注意在word文檔中,如果兩個相近的字元樣式不同,word預設會儲存在不同的RUN元素中,由此很多朋友在配置好以後都需要儲存為一個單獨的檔案,然後不把不在一起的標籤合并到一個RUN元素中,如果檔案比較大,我相信這絕對是一個比較痛苦的事情,這裡將會側重處理這個問題.我的解決方案是只保留第一RUN的樣式其他的刪掉
解析word模板
首先需要將檔案轉換為XWPFDocument對象,可以通過流的當時,也可以通過opcpackage,不過如果使用opcpackage開啟的方式,開啟的檔案和最終產生的檔案不能夠是同一個檔案,我這裡採用檔案流的方式
public XWPFDocument openDocument() { XWPFDocument xdoc = null; InputStream is = null; try { is = new FileInputStream(saveFile); xdoc = new XWPFDocument(is); } catch (IOException e) { e.printStackTrace(); } return xdoc; }
擷取非列表的標籤,實現方式XWPFDocument對象有當前所有段落以及表格,這裡暫不考慮表格巢狀表格格的情況,每個段落的文本資訊是可以通過p.getText()擷取,擷取段落中文檔配置資訊如下:
// 擷取段落集合中所有文本 public List<TagInfo> getWordTag(XWPFDocument doc, String regex) { List<TagInfo> tags = new ArrayList<TagInfo>(); // 普通段落 List<XWPFParagraph> pars = doc.getParagraphs(); for (int i = 0; i < pars.size(); i++) { XWPFParagraph p = pars.get(i); setTagInfoList(tags, p, regex); } // Table中段落 List<XWPFTable> commTables = getDocTables(doc, false, regex); for (XWPFTable table : commTables) { List<XWPFParagraph> tparags = getTableParagraph(table); for (int i = 0; i < tparags.size(); i++) { XWPFParagraph p = tparags.get(i); setTagInfoList(tags, p, regex); } } return tags; }
擷取文本後通過正則解析,並依次儲存到TagInfo中
// 向 taglist中添加新解析的段落資訊 private void setTagInfoList(List<TagInfo> list, XWPFParagraph p, String regex) { if (regex == "") regex = defaultRegex; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(p.getText()); int startPosition = 0; while (matcher.find(startPosition)) { String match = matcher.group(); if (!list.contains(new TagInfo(match, match, ""))) { list.add(new TagInfo(match, match, "")); } startPosition = matcher.end(); } }
解析表格
// 擷取Table列表中的配置資訊 public Map<String, List<List<TagInfo>>> getTableTag(XWPFDocument doc, String regex) { Map<String, List<List<TagInfo>>> mapList = new HashMap<String, List<List<TagInfo>>>(); List<XWPFTable> lstTables = getDocTables(doc, true, regex); for (XWPFTable table : lstTables) { // 擷取每個表格第一個儲存格,以及最後一行 String strTableName = getTableListName(table, regex); List<List<TagInfo>> list = new ArrayList<List<TagInfo>>(); List<TagInfo> lstTag = new ArrayList<TagInfo>(); int rowSize = table.getRows().size(); XWPFTableRow lastRow = table.getRow(rowSize - 1); for (XWPFTableCell cell : lastRow.getTableCells()) { for (XWPFParagraph p : cell.getParagraphs()) { // 去掉空白字串 if (p.getText() != null && p.getText().length() > 0) { setTagInfoList(lstTag, p, regex); } } } list.add(lstTag); // 添加到資料集 mapList.put(strTableName, list); } return mapList; }
產生WORD文檔
痛點替換標籤
傳入資料格式包含三個formtag以及一個tableTag
{"formTags":
[{"TagName":"${xxxx}","TagText":"${xxxx}","TagValue":""},
{"TagName":"${123}","TagText":"${123}","TagValue":""},
{"TagName":"${ddd}","TagText":"${ddd}","TagValue":""}],
"tableTags":{
"${table}":[
[{"TagName":"${COL1}","TagText":"${COL1}","TagValue":""},{"TagName":"${COL2}","TagText":"${COL2}","TagValue":""}]
]}
}
普通文檔產生,並且保留配置樣式,這裡主要使用POI中提供searchText方法,返回Tag所有所在的RUN標籤,通過一個字元做比較,如果找的第一個匹配的文本開始計數,所有在當前條件下類型 $${xxx}這樣的標籤是無法實現替換的
替換普通文本Tag
public void ReplaceInParagraph(List<TagInfo> tagList, XWPFParagraph para, String regex) { if (regex == "") regex = defaultRegex; List<XWPFRun> runs = para.getRuns(); for (TagInfo ti : tagList) { String find = ti.TagText; String replValue = ti.TagValue; TextSegement found = para.searchText(find, new PositionInParagraph()); if (found != null) { // 判斷尋找內容是否在同一個Run標籤中 if (found.getBeginRun() == found.getEndRun()) { XWPFRun run = runs.get(found.getBeginRun()); String runText = run.getText(run.getTextPosition()); String replaced = runText.replace(find, replValue); run.setText(replaced, 0); } else { // 存在多個Run標籤 StringBuilder sb = new StringBuilder(); for (int runPos = found.getBeginRun(); runPos <= found .getEndRun(); runPos++) { XWPFRun run = runs.get(runPos); sb.append(run.getText((run.getTextPosition()))); } String connectedRuns = sb.toString(); String replaced = connectedRuns.replace(find, replValue); XWPFRun firstRun = runs.get(found.getBeginRun()); firstRun.setText(replaced, 0); // 刪除後邊的run標籤 for (int runPos = found.getBeginRun() + 1; runPos <= found .getEndRun(); runPos++) { // 清空其他標籤內容 XWPFRun partNext = runs.get(runPos); partNext.setText("", 0); } } } } // 完成第一遍尋找,檢測段落中的標籤是否已經替換完 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(para.getText()); boolean find = matcher.find(); if (find) { ReplaceInParagraph(tagList, para, regex); find = false; } }
表格主要是通過複製模版行,然後對模版行中的內容做修改
複製文字標籤RUN
private void CopyRun(XWPFRun target, XWPFRun source) { target.getCTR().setRPr(source.getCTR().getRPr()); // 設定文本 target.setText(source.text()); }
複製段落XWPFParagraph
private void copyParagraph(XWPFParagraph target, XWPFParagraph source) { // 設定段落樣式 target.getCTP().setPPr(source.getCTP().getPPr()); // 添加Run標籤 for (int pos = 0; pos < target.getRuns().size(); pos++) { target.removeRun(pos); } for (XWPFRun s : source.getRuns()) { XWPFRun targetrun = target.createRun(); CopyRun(targetrun, s); } }
複製儲存格XWPFTableCell
private void copyTableCell(XWPFTableCell target, XWPFTableCell source) { // 列屬性 target.getCTTc().setTcPr(source.getCTTc().getTcPr()); // 刪除目標 targetCell 所有儲存格 for (int pos = 0; pos < target.getParagraphs().size(); pos++) { target.removeParagraph(pos); } // 添加段落 for (XWPFParagraph sp : source.getParagraphs()) { XWPFParagraph targetP = target.addParagraph(); copyParagraph(targetP, sp); } }
複製行XWPFTableRow
private void CopytTableRow(XWPFTableRow target, XWPFTableRow source) { // 複製樣式 target.getCtRow().setTrPr(source.getCtRow().getTrPr()); // 複製儲存格 for (int i = 0; i < target.getTableCells().size(); i++) { copyTableCell(target.getCell(i), source.getCell(i)); } }
著作權,轉載請說明來源 楊瀚博
以上就完成所有功能更,只要你配置規範,可以完全原樣輸出模版內容。這裡特別感謝下肖哥哥大力支援。
其次,java的編碼真的讓人很無語,get或post時中文各種亂碼