文章目录
需求
做项目的时候有这样一个需求,我们现在有两个word(docx)文档,一个文档里面放着正文内容,另一个文档是模板,需求是需要将正文内容同时包含样式复制到另一个模板里,因为正文文档里还开启了修订,要求复制过去以后不带着修订,直接显示修订后的内容,完成需求的第一个工作就是做技术选型,选来选去还是选择poi,因为poi的资料相对来说多一些,虽然代码比较复杂一些,但是类名什么的还是有一定规律的容易熟悉。
代码
首先我们需要了解一些关于docx的知识,才能更好的了解下面的代码,根据我对poi的简单的了解,poi操作word应该主要是基于xml的,所以我们需要先了解一下docx文档的xml结构才能更好的去用poi完成需求,我们先打开一个docx文档然后选择另存为xml如下图:
这是一个测试文档,带着样式和修订内容
这是模板文档,带着”正文”两个字作为标记,我们要在模板里找到正文两个字,然后把正文两个字删除以后再正文这个段落上开始填充内容
文件格式选择为word xml文档保存以后打开保存的xml文档,打开以后百度一个xml在线格式化工具格式化一下以后变成这样,我们搜索一下我们标记的内容然后就能看到标签了
其实还有一个ins标签。。代表着修订线。。了解了标签下面可以直接上代码了。。。代码没有多难。。还是很简单的。。只不过我在做的时候发现这方面的资料很少,所以就把我的第一次博客交给它了。
首先我们还需要引三个jar包
<!-- poi jar包 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/ooxml-schemas -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.1</version>
</dependency>
这里的三个jar包最主要的应该是ooxml-schemas这个jar包,因为poi-ooxml-schemas这个jar包是精简版的jar,里面有很多的类都没有,所以我们需要引入完全版的ooxml-schemas jar包,这个jar包是需要对应poi版本的,3.13版本的poi对应的是1.1版本的ooxml-schemas
package cn.gov.sach.cascontent.poi;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
public class PoiConvertWord {
private byte[] TemplateByte;
public PoiConvertWord() {
try {
FileInputStream redTemplateInputStream = new FileInputStream(new File("/Users/gao/Desktop/测试套红/套红模板.docx"));
this.TemplateByte = new byte[redTemplateInputStream.available()];
redTemplateInputStream.read(TemplateByte);
} catch (Exception e) {
e.printStackTrace();
}
}
public void extractText() {
try {
FileInputStream fileInputStream = new FileInputStream(new File("/Users/gao/Desktop/测试套红/正文.docx"));
XWPFDocument mainDocument = new XWPFDocument(fileInputStream);
List<XWPFParagraph> paragraphs = mainDocument.getParagraphs();//获取读取模板里的所有段落对象
this.setRed(paragraphs);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setRed(List<XWPFParagraph> paraList) throws Exception {
XWPFDocument xwpfDocument;
int startPoint = 0;
//读取模板
if (TemplateByte.length == 0) {
return;
}
xwpfDocument = new XWPFDocument(new ByteArrayInputStream(TemplateByte));//构建模板文档对象
List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();//获取模板里的段落对象
for (int i=0;i<paragraphs.size();i++) {//遍历模板中的段落对象找到标记位置
XWPFParagraph xwpfParagraph = paragraphs.get(i);
List<XWPFRun> runs = xwpfParagraph.getRuns();
for (int l=0;l<runs.size();l++) {
XWPFRun run = runs.get(l);
if (run.text().equals("正文")) {//如果遍历到标记位置退出循环,并记录一下标记位置所在段落对象的下标
xwpfParagraph.removeRun(l);//同时把正文两字移除
startPoint = i;
break;//退出遍历run的循环
}
}
if (startPoint!=0) {
break;//退出遍历paragraph的循环
}
}
if (startPoint!=0) {
XWPFParagraph insertNewParagraph = paragraphs.get(startPoint);//获取到标记位置的段落对象
XmlCursor xmlCursor = insertNewParagraph.getCTP().newCursor();//获取到段落的光标
for (int j=0;j<paraList.size();j++) {
XWPFParagraph oldParagraph = paraList.get(j);//获得传过来的段落对象
CTPPr oldPPr = oldParagraph.getCTP().getPPr();//获取段落对象的样式的ppr标签
if (oldPPr!=null) {
CTParaRPr oldPPrRpr = oldPPr.getRPr();//获取ppr标签里的rpr标签
CTPPrChange oldPPrChange = oldPPr.getPPrChange();//获取pprchange标签
CTTrackChange rprIns = null;
CTParaRPrChange oldPraRprChange = null;
if (oldPPrRpr!=null) {
rprIns = oldPPrRpr.getIns();//获取ins标签
oldPraRprChange = oldPPrRpr.getRPrChange();//获取ppr标签中的rpr标签里的rprchange标签
}
if (oldPPrChange!=null) {//如果有change标签证明是修订内容,设置为取消修订
oldPPr.unsetPPrChange();
}
if (oldPraRprChange!=null) {
oldPPrRpr.unsetRPrChange();
}
if (rprIns!=null) {//ins标签代表着修订线,如果有修订线则取消修订线
oldPPrRpr.unsetIns();
}
}
insertNewParagraph.getCTP().setPPr(oldPPr);//把传过来的段落中的ppr标签设置到新的段落里
List<XWPFRun> runs = oldParagraph.getRuns();//获取run对象
for (int k=0;k<runs.size();k++) {
XWPFRun oldRun = runs.get(k);
CTRPr oldRPr = oldRun.getCTR().getRPr();
CTRPrChange oldRPrChange = oldRPr.getRPrChange();//run标签里也有rprchange标签也需要像上面一样设置
if (oldRPrChange!=null) {
oldRPr.unsetRPrChange();
}
XWPFRun newRun = insertNewParagraph.createRun();
newRun.getCTR().setRPr(oldRPr);
newRun.setText(oldRun.text());
insertNewParagraph.addRun(newRun);
}
xmlCursor.toNextSibling();//下移光标
insertNewParagraph = xwpfDocument.insertNewParagraph(xmlCursor);//在当前光标处插入新的段落
xmlCursor = insertNewParagraph.getCTP().newCursor();
}
}
FileOutputStream fileOutputStream = new FileOutputStream("/Users/gao/Desktop/测试.docx");
xwpfDocument.write(fileOutputStream);
}
public static void main(String[] args) {
PoiConvertWord poiConvertWord = new PoiConvertWord();
poiConvertWord.extractText();
}
}
代码复制过去稍微改一改路径就可以用了,代码大部分样式都能够保留,不保证能完美的保留所有的样式。。。