背景
现在有一个需求,需要将多个word文档合并为一个文档,并使用poi-el渲染模板生成一个新的文档。
在网上找了很多,没找到合适的,最后摸索出一个可行方案,基于XWPFDocument.getBodyElements方法的。
其实一开始考虑过这个方法,但当时对底层源码不熟悉,觉得无法分辨每个element的类型,比如chart对象其实是包含在XWPFParagraph对象中的,所以当时排除了这个方法。后来在网上找到了基于OPCPackage的,但是这种方法无法保证顺序,所以也排除了。最后想到了XWPFTemplate.render方法的底层源码,可以完美解决getBodyElements的类型问题,所以就使用了getBodyElements这个方案。
参考文档:https://codeleading.com/article/50704657377/
方案思路
使用XWPFDocument.getBodyElements()
来获取整个doc的所有元素对象,元素对象供分为三类,分别为PARAGRAPH、TABLE、CONTENTCONTROL,其中,Picture、Chart均是包含在PARAGRAPH里的,关于这块可以参考底层代码TemplateResolver
完整代码
java
package word;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.xwpf.CTDrawingWrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 将多个word文档合并为一个word文件
*/
public class CopyDoc{
static String target = "C:\\Users\\huahua\\Desktop\\template.docx";
static String dest = "C:\\Users\\huahua\\Desktop\\out.docx";
public static List<XWPFDocument> readDoc(List<String> paths) throws Exception {
List<XWPFDocument> documents = new ArrayList();
for (String path : paths) {
documents.add(new XWPFDocument(new FileInputStream(path)));
}
return documents;
}
public static void main(String[] args) throws Exception {
List<String> paths = new ArrayList() {{
add("C:\\Users\\huahua\\Desktop\\模板\\11.docx");
add("C:\\Users\\huahua\\Desktop\\模板\\12.docx");
add("C:\\Users\\huahua\\Desktop\\模板\\14.docx");
}};
List<XWPFDocument> document = readDoc(paths);
insertContext(document, new ArrayList(){{
add("标题1");
add("标题2");
add("标题3");
}});
renderData();
}
public static void renderData() throws IOException {
XWPFTemplate compile = XWPFTemplate.compile(target);
compile.render(new HashMap() {
{
put("1", "ajsdkaklsjd");
put("2", "ajsdkaklsjd");
put("3", "ajsdkaklsjd");
}
}).writeToFile(dest);
}
/**
* 追加内容到页面
*
* @throws IOException
*/
public static void insertContext(List<XWPFDocument> documents, List<String> titles) throws Exception {
//模板文档
XWPFDocument document1 = new XWPFDocument();
XWPFDocument template = new XWPFDocument(new FileInputStream("C:\\Users\\huahua\\Desktop\\标题文档.docx"));
// 获得模板文档的整体样式
CTStyles wordStyles = template.getStyle();
// 获取新建文档对象的样式
XWPFStyles newStyles = document1.createStyles();
// 关键行// 修改设置文档样式为静态块中读取到的样式
newStyles.setStyles(wordStyles);
int i = 1;
for (XWPFDocument document : documents) {
//一级标题
XWPFParagraph paragraph = document1.createParagraph();
paragraph.setStyle("2");
XWPFRun run1 = paragraph.createRun();
run1.addBreak();
run1.setText(i+" ."+titles.get(i-1));
Document document2 = new XWPFDocument();
List<IBodyElement> bodyElements = document.getBodyElements();
int chartPos = 0;
for (IBodyElement element : bodyElements) {
if (element.getElementType() == BodyElementType.PARAGRAPH) {
XWPFParagraph sourcePara = (XWPFParagraph) element;
XWPFParagraph targetParagraph = document1.createParagraph();
copyPara(targetParagraph, sourcePara, document1, document, chartPos);
} else if (element.getElementType() == BodyElementType.TABLE) {
XWPFTable sourceTable = (XWPFTable) element;
List<XWPFTableRow> rows = sourceTable.getRows();
if (null == rows) continue;
copyTable(sourceTable, document1);
}
}
i++;
}
FileOutputStream fopts = new FileOutputStream(target);
document1.write(fopts);
document1.close();
fopts.close();
}
/**
* @param targetParagraph
* @param sourcePara
*/
private static void copyPara(XWPFParagraph targetParagraph, XWPFParagraph sourcePara, XWPFDocument document, XWPFDocument srcDoc, int pos) throws Exception {
//把传过来的段落中的ppr标签设置到新的段落里
CTP ctp = targetParagraph.getCTP();
ctp.setPPr(sourcePara.getCTPPr());
List<XWPFRun> runs = sourcePara.getRuns();//获取run对象
for (int k = 0; k < runs.size(); k++) {
XWPFRun newRun = targetParagraph.createRun();
XWPFRun oldRun = runs.get(k);
CTRPr oldRPr = oldRun.getCTR().getRPr();
newRun.getCTR().setRPr(oldRPr);
//处理图表
if (StringUtils.isBlank(oldRun.getText(0))) {
CTR ctr = oldRun.getCTR();
CTDrawing ctDrawing = CollectionUtils.isNotEmpty(ctr.getDrawingList()) ? ctr.getDrawingArray(0) : null;
if (null == ctDrawing) continue;
CTDrawingWrapper wrapper = new CTDrawingWrapper(ctDrawing);
String rid = wrapper.getChartId();
if (null == rid) continue;
POIXMLDocumentPart documentPart = oldRun.getDocument().getRelationById(rid);
if (null == documentPart || !(documentPart instanceof XWPFChart)) continue;
XWPFChart chart1 = srcDoc.getCharts().get(pos);
XWPFChart chart = document.createChart(newRun, 7 * Units.EMU_PER_CENTIMETER, 7 * Units.EMU_PER_CENTIMETER);
CTPlotArea plotArea = chart1.getCTChart().getPlotArea();
chart.getCTChart().setPlotArea(plotArea);
chart.getCTChart().setLegend(chart1.getCTChart().getLegend());
chart.setWorkbook(chart1.getWorkbook());
CTNonVisualDrawingProps docPr = newRun.getCTR().getDrawingList().get(0).getInlineList().get(0).getDocPr();
if (StringUtils.isBlank(docPr.getTitle())) {
docPr.setTitle("{{test}}");
}
pos++;
} else {
newRun.setText(oldRun.getText(0));
}
targetParagraph.addRun(newRun);
}
}
/**
* 复制表格
*
* @param source
* @param document
*/
private static void copyTable(XWPFTable source, XWPFDocument document) {
// 创建新的 CTTbl
CTTbl ctTbl = CTTbl.Factory.newInstance();
// 复制原来的CTTbl
ctTbl.set(source.getCTTbl());
IBody iBody = source.getBody();
XWPFTable newTable = new XWPFTable(ctTbl, iBody);
document.createTable();
document.setTable(document.getTables().size()-1, newTable);
}
}