如果遇到poi读取例如{name}不能识别为一个整体,可以使用word的域操作,如果不太清楚域的使用,可以这么操作,先在text文档中写好,例如{name},然后再整个复制到word中,不要一个一个在word中敲,不然有可能不会被poi识别为一个整体
xwpfdocument对象
poi是apache提供的可以操作word文档的第三方jar。poi能操作word是使用xwpfdocument对象。
- xwpfdocument对象可以解析docx文件,在xwpfdocument对象通过输入流解析docx的时候,会获取到docx文档中的各种对象,例如表格,段落,图片等,通过操作xwpfdocument对象就可以修改模板内容
- xwpfdocument api结构org.apache.poi.xwpf.usermodel.xwpfdocument
- xwpfdocument 提供write(outputstream stream)方法将修改后的对象重新写入xml并生成新的docx
通过xwpfdocument 可以获得的docx中的各种对象
要具体操作通过xwpfdocument 可以获得的docx中的各种对象,我们离不开一个对象为xwpfrun对象,api结构
org.apache.poi.xwpf.usermodel.xwpfrun。其描述为:xwpfrun object defines a region of text with a common set of properties。通过描述我们不难理解其作用为设置文本对象的各种属性。
通过xwpfdocument 获取对象
//解析docx模板并获取document对象
xwpfdocument document = new xwpfdocument(poixmldocument.openpackage(inputurl));
//获取整个文本对象
list<xwpfparagraph> allparagraph = document.getparagraphs();
//获取整个表格对象
list<xwpftable> alltable = document.gettables();
//获取图片对象
xwpfpicturedata pic = document.getpicturedatabyid("picid");
首先建一个很简单的word模板001.docx,我们通过操作对象获取word中的文本内容
下面demo的输出可以看出我们操作文本对象,成功获取了文本内容
@component("xwpruntest")
public class xwpruntest {
//模板文件地址
private static string inputurl = "c:\users\zhihe\desktop\demo\001.docx";
public void runtest(){
try {
//解析docx模板并获取document对象
xwpfdocument document = new xwpfdocument(poixmldocument.openpackage(inputurl));
//获取整个文本对象
list<xwpfparagraph> allparagraph = document.getparagraphs();
//获取xwpfrun对象输出整个文本内容
stringbuffer temptext = new stringbuffer();
for (xwpfparagraph xwpfparagraph : allparagraph) {
list<xwpfrun> runlist = xwpfparagraph.getruns();
for (xwpfrun xwpfrun : runlist) {
temptext.append(xwpfrun.tostring());
}
}
system.out.println(temptext.tostring());
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
}
测试
@runwith(springjunit4classrunner.class)
@contextconfiguration("classpath:applicationcontext.xml")
public class runtest {
@resource
private xwpruntest xwpruntest;
@test
public void runtest(){
xwpruntest.runtest();
}
}
控制台输出结果
在这里发现操作文本对象的时候并没有获取到表格文本,所以如果我们需要获取到表格文本还需要另外的操作
@component("xwpruntabletest")
public class xwpruntabletest {
//模板文件地址
private static string inputurl = "c:\users\zhihe\desktop\demo\001.docx";
public void tabletest(){
try {
stringbuffer tabletext = new stringbuffer();
//解析docx模板并获取document对象
xwpfdocument document = new xwpfdocument(poixmldocument.openpackage(inputurl));
//获取全部表格对象
list<xwpftable> alltable = document.gettables();
for (xwpftable xwpftable : alltable) {
//获取表格行数据
list<xwpftablerow> rows = xwpftable.getrows();
for (xwpftablerow xwpftablerow : rows) {
//获取表格单元格数据
list<xwpftablecell> cells = xwpftablerow.gettablecells();
for (xwpftablecell xwpftablecell : cells) {
list<xwpfparagraph> paragraphs = xwpftablecell.getparagraphs();
for (xwpfparagraph xwpfparagraph : paragraphs) {
list<xwpfrun> runs = xwpfparagraph.getruns();
for(int i = 0; i < runs.size();i++){
xwpfrun run = runs.get(i);
tabletext.append(run.tostring());
}
}
}
}
}
system.out.println(tabletext.tostring());
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
}
测试
成功获取表格
下面我们来对一个wrod进行简单的修改,首先有个模板word,里面只有几个字
代码
public class firstwordtest {
//模板文件地址
private static string inputurl = "c:\users\zhihe\desktop\demo\001.docx";
//新生产的模板文件
private static string outputurl = "c:\users\zhihe\desktop\demo\test.docx";
/**
*
* @param inputurl 模板路径
* @param outputurl 模板保存路径
*/
public static void changeword(string inputurl, string outputurl ){
try {
//获取word文档解析对象
xwpfdocument doucument = new xwpfdocument(poixmldocument.openpackage(inputurl));
//获取段落文本对象
list<xwpfparagraph> paragraph = doucument.getparagraphs();
//获取首行run对象
xwpfrun run = paragraph.get(0).getruns().get(0);
//设置文本内容
run.settext("修改了的word");
//生成新的word
file file = new file(outputurl);
fileoutputstream stream = new fileoutputstream(file);
doucument.write(stream);
stream.close();
} catch (ioexception e) {
// todo auto-generated catch block
e.printstacktrace();
}
}
public static void main(string[] args) {
changeword(inputurl,outputurl);
}
}
测试
运行后生成新的word
但是在实际项目中并没有这么简单,模板文档中可能需要替换文本中的文字,也可能需要替换表格对象中的文字,或者在指定表格中插入数据,下面我们就仿照实际情况来做个简单的模板。
首先创建一个word的模板
工具类
package com.lovo.utils.wordtopdf;
import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.util.arraylist;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import java.util.map.entry;
import java.util.set;
import org.apache.poi.poixmldocument;
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;
/**
* 通过word模板生成新的word工具类
*
* @author zhiheng
*
*/
public class wordertonewwordutils {
/**
* 根据模板生成新word文档
* 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
* @param inputurl 模板存放地址
* @param outputurl 新文档存放地址
* @param textmap 需要替换的信息集合
* @param tablelist 需要插入的表格信息集合
* @return 成功返回true,失败返回false
*/
public static boolean changword(string inputurl, string outputurl,
map<string, string> textmap, list<string[]> tablelist) {
//模板转换默认成功
boolean changeflag = true;
try {
//获取docx解析对象
xwpfdocument document = new xwpfdocument(poixmldocument.openpackage(inputurl));
//解析替换文本段落对象
wordertonewwordutils.changetext(document, textmap);
//解析替换表格对象
wordertonewwordutils.changetable(document, textmap, tablelist);
//生成新的word
file file = new file(outputurl);
fileoutputstream stream = new fileoutputstream(file);
document.write(stream);
stream.close();
} catch (ioexception e) {
e.printstacktrace();
changeflag = false;
}
return changeflag;
}
/**
* 替换段落文本
* @param document docx解析对象
* @param textmap 需要替换的信息集合
*/
public static void changetext(xwpfdocument document, map<string, string> textmap){
//获取段落集合
list<xwpfparagraph> paragraphs = document.getparagraphs();
for (xwpfparagraph paragraph : paragraphs) {
//判断此段落时候需要进行替换
string text = paragraph.gettext();
if(checktext(text)){
list<xwpfrun> runs = paragraph.getruns();
for (xwpfrun run : runs) {
//替换模板原来位置
run.settext(changevalue(run.tostring(), textmap),0);
}
}
}
}
/**
* 替换表格对象方法
* @param document docx解析对象
* @param textmap 需要替换的信息集合
* @param tablelist 需要插入的表格信息集合
*/
public static void changetable(xwpfdocument document, map<string, string> textmap,
list<string[]> tablelist){
//获取表格对象集合
list<xwpftable> tables = document.gettables();
for (int i = 0; i < tables.size(); i++) {
//只处理行数大于等于2的表格,且不循环表头
xwpftable table = tables.get(i);
if(table.getrows().size()>1){
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if(checktext(table.gettext())){
list<xwpftablerow> rows = table.getrows();
//遍历表格,并替换模板
eachtable(rows, textmap);
}else{
// system.out.println("插入"+table.gettext());
inserttable(table, tablelist);
}
}
}
}
/**
* 遍历表格
* @param rows 表格行对象
* @param textmap 需要替换的信息集合
*/
public static void eachtable(list<xwpftablerow> rows ,map<string, string> textmap){
for (xwpftablerow row : rows) {
list<xwpftablecell> cells = row.gettablecells();
for (xwpftablecell cell : cells) {
//判断单元格是否需要替换
if(checktext(cell.gettext())){
list<xwpfparagraph> paragraphs = cell.getparagraphs();
for (xwpfparagraph paragraph : paragraphs) {
list<xwpfrun> runs = paragraph.getruns();
for (xwpfrun run : runs) {
run.settext(changevalue(run.tostring(), textmap),0);
}
}
}
}
}
}
/**
* 为表格插入数据,行数不够添加新行
* @param table 需要插入数据的表格
* @param tablelist 插入数据集合
*/
public static void inserttable(xwpftable table, list<string[]> tablelist){
//创建行,根据需要插入的数据添加新行,不处理表头
for(int i = 1; i < tablelist.size(); i++){
xwpftablerow row =table.createrow();
}
//遍历表格插入数据
list<xwpftablerow> rows = table.getrows();
for(int i = 1; i < rows.size(); i++){
xwpftablerow newrow = table.getrow(i);
list<xwpftablecell> cells = newrow.gettablecells();
for(int j = 0; j < cells.size(); j++){
xwpftablecell cell = cells.get(j);
cell.settext(tablelist.get(i-1)[j]);
}
}
}
/**
* 判断文本中时候包含$
* @param text 文本
* @return 包含返回true,不包含返回false
*/
public static boolean checktext(string text){
boolean check = false;
if(text.indexof("$")!= -1){
check = true;
}
return check;
}
/**
* 匹配传入信息集合与模板
* @param value 模板需要替换的区域
* @param textmap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static string changevalue(string value, map<string, string> textmap){
set<entry<string, string>> textsets = textmap.entryset();
for (entry<string, string> textset : textsets) {
//匹配模板与替换值 格式${key}
string key = "${"+textset.getkey()+"}";
if(value.indexof(key)!= -1){
value = textset.getvalue();
}
}
//模板未匹配到区域替换为空
if(checktext(value)){
value = "";
}
return value;
}
public static void main(string[] args) {
//模板文件地址
string inputurl = "c:\users\zhihe\desktop\demo\001.docx";
//新生产的模板文件
string outputurl = "c:\users\zhihe\desktop\demo\test.docx";
map<string, string> testmap = new hashmap<string, string>();
testmap.put("name", "小明");
testmap.put("sex", "男");
testmap.put("address", "软件园");
testmap.put("phone", "88888888");
list<string[]> testlist = new arraylist<string[]>();
testlist.add(new string[]{"1","1aa","1bb","1cc"});
testlist.add(new string[]{"2","2aa","2bb","2cc"});
testlist.add(new string[]{"3","3aa","3bb","3cc"});
testlist.add(new string[]{"4","4aa","4bb","4cc"});
wordertonewwordutils.changword(inputurl, outputurl, testmap, testlist);
}
}
测试
这么我们就实现了个简单的poi操作模板完成替换和插入的功能,本来还准备实现固定位置插入图片的功能,不过发现这是个巨坑,暂时未实现其功能,等以后有空再进行完善,此代码以docx格式进行演示操作
如果遇到poi读取例如{name}不能识别为一个整体,可以使用word的域操作,如果不太清楚域的使用,可以这么操作,先在text文档中写好,例如{name},然后再整个复制到word中,不要一个一个在word中敲,不然有可能不会被poi识别为一个整体