用poi操作docx文档将文档内容保留样式、去掉修订同时复制到另一个docx模板中

文章目录

需求

做项目的时候有这样一个需求,我们现在有两个word(docx)文档,一个文档里面放着正文内容,另一个文档是模板,需求是需要将正文内容同时包含样式复制到另一个模板里,因为正文文档里还开启了修订,要求复制过去以后不带着修订,直接显示修订后的内容,完成需求的第一个工作就是做技术选型,选来选去还是选择poi,因为poi的资料相对来说多一些,虽然代码比较复杂一些,但是类名什么的还是有一定规律的容易熟悉。

代码

首先我们需要了解一些关于docx的知识,才能更好的了解下面的代码,根据我对poi的简单的了解,poi操作word应该主要是基于xml的,所以我们需要先了解一下docx文档的xml结构才能更好的去用poi完成需求,我们先打开一个docx文档然后选择另存为xml如下图:
《用poi操作docx文档将文档内容保留样式、去掉修订同时复制到另一个docx模板中》这是一个测试文档,带着样式和修订内容
《用poi操作docx文档将文档内容保留样式、去掉修订同时复制到另一个docx模板中》这是模板文档,带着”正文”两个字作为标记,我们要在模板里找到正文两个字,然后把正文两个字删除以后再正文这个段落上开始填充内容
《用poi操作docx文档将文档内容保留样式、去掉修订同时复制到另一个docx模板中》
文件格式选择为word xml文档保存以后打开保存的xml文档,打开以后百度一个xml在线格式化工具格式化一下以后变成这样,我们搜索一下我们标记的内容然后就能看到标签了
《用poi操作docx文档将文档内容保留样式、去掉修订同时复制到另一个docx模板中》其实还有一个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();
	}


}

代码复制过去稍微改一改路径就可以用了,代码大部分样式都能够保留,不保证能完美的保留所有的样式。。。

    原文作者:一个要命不要钱的程序员
    原文地址: https://blog.csdn.net/qq_36331127/article/details/84798301
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞