摘要:[JAVA] word 透過 template 產生報表 (poi.xwpf.usermodel.*)
主要是利用POI 抓取特定關鍵字並加以取代
快速產生報表,適用于報表格式較為複雜的word,
先製作好準備產生報表的樣式,有個很重要的地方在於
看似相同的${title} 字串,實際word中可能拆成多個段落(Paragraph)
所以建議事先透過xml分析器(ex: XMLspy)將取代字元檢查一下,是否符合于預期的排列方式.
先取得template
String tempPath = new StringBuilder().append("META-INF/report-template/").append(templateName).append(".docx").toString();
InputStream b03TempInput = ReportUtil.class.getClassLoader().getResourceAsStream(tempPath);
並產生一個新的XWPFDocument,帶入此template.
XWPFDocument doc = null;
try {
doc = new XWPFDocument(b03TempInput);
...
將資料已map方式填入
// 將欲填入資料放進map
Map map = new HashMap();
map.put("heading",“heading”);
map.put("recorder","xxx"); // recorder
...
定義pattern 此範例為${key值}
// 建立取代的pattern (正則表示式)
String pattern = "";
int first = 0;
for (String str : map.keySet()) {
if (first > 0) {
pattern += "|";
}
pattern = pattern + "(?<=\\$\\{)" + str + "(?=\\})";
first++;
}
Pattern pat = Pattern.compile(pattern);
開始針對xml格式進行搜尋並取代key
// 針對段落模板取代
Iterator itrPs = doc.getParagraphsIterator();
while (itrPs.hasNext()) {
XWPFParagraph paragraph = itrPs.next();
for (XWPFRun run : paragraph.getRuns()) {
for (CTText text : run.getCTR().getTList()) {
matcher = pat.matcher(text.getStringValue());
if (matcher.find()) {
key = matcher.group();
text.setStringValue(text.getStringValue().
replaceAll("\\$\\{"+ key + "\\}", map.get(key)));
}
}
}
}
// 針對表單模板取代 (若無表單 可直接針對段落search)
Iterator itrTable = doc.getTablesIterator();
while (itrTable.hasNext()) {
XWPFTable table = itrTable.next();
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
this.fillUpParagraph(cell, pat, map);
}
}
}
private void fullUpParagraph(XWPFTableCell cell, Pattern pat, Map map) {
XWPFDocument tempDoc = new XWPFDocument();
Matcher matcher;
String key;
List tempParagraphList = new ArrayList();
// 取得每個段落
for (XWPFParagraph paragraph : cell.getParagraphs()) {
// 取得每個Run
for (XWPFRun run : paragraph.getRuns()) {
// 分析每個run中的 text
for (CTText text : run.getCTR().getTList()) {
matcher = pat.matcher(text.getStringValue());
if (matcher.find()) {
key = matcher.group();
if (map.get(key) != null) {
String[] paragraphRows = map.get(key).split("\n");
for (int i = 0; i < paragraphRows.length; i++) {
if (i == 0) {
text.setStringValue(text.getStringValue().replaceAll("\\$\\{" + key + "\\}",
paragraphRows[i]));
} else {
XWPFParagraph newParagraph = tempDoc.createParagraph();
XWPFRun newRun = newParagraph.createRun();
newRun.setText(paragraphRows[i]);
tempParagraphList.add(newParagraph);
}
}
}
}
}
}
}
備註 : 有時會因為套用模板而 多一頁空白頁 可以加一個Break
//將多個doc中的表單組合成一個最後的報表
for (XWPFDocument doc : docList) {
for (XWPFTable table : doc.getTables()) {
finalDoc.createTable();
finalDoc.setTable(count, table);
XWPFParagraph paragraph = finalDoc.createParagraph();
XWPFRun run = paragraph.createRun();
// 避免最後一個doc多一頁空白頁
if (count != docList.size() - 1) {
run.addBreak(BreakType.PAGE);
}
count++;
}
}