项目系统中打印功能,导出 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 公众号