|
@@ -3,7 +3,9 @@ package com.yaoyicloud.tools;
|
|
|
import java.io.FileNotFoundException;
|
|
|
import java.io.FileOutputStream;
|
|
|
import java.io.IOException;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
|
|
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
import org.apache.poi.xwpf.usermodel.IBodyElement;
|
|
@@ -12,39 +14,142 @@ import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFPicture;
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFStyle;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFStyles;
|
|
|
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.drawingml.x2006.main.CTBlip;
|
|
|
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
|
|
|
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
|
|
|
|
|
|
import com.deepoove.poi.xwpf.XWPFStructuredDocumentTag;
|
|
|
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyle;
|
|
|
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STStyleType;
|
|
|
|
|
|
public final class DocxUtil {
|
|
|
|
|
|
public static void mergeDocx(XWPFDocument target, List<XWPFDocument> sources, String outputPath)
|
|
|
throws InvalidFormatException, FileNotFoundException, IOException {
|
|
|
- mergeDocx(target, sources, outputPath, true);
|
|
|
+ mergeDocx(target, sources, outputPath, false);
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * 只更新target的内容,全局设定(fonts, styles, custom items)都使用target docx
|
|
|
- */
|
|
|
public static void mergeDocx(XWPFDocument target, List<XWPFDocument> sources, String outputPath,
|
|
|
boolean clearTargetBody) throws InvalidFormatException, FileNotFoundException, IOException {
|
|
|
if (clearTargetBody) {
|
|
|
target.getDocument().unsetBody();
|
|
|
target.getDocument().addNewBody();
|
|
|
}
|
|
|
- for (XWPFDocument source : sources) {
|
|
|
+
|
|
|
+ // 2. 处理每个源文档的样式并合并
|
|
|
+ for (int i = 0; i < sources.size(); i++) {
|
|
|
+ XWPFDocument source = sources.get(i);
|
|
|
+ String sourcePrefix = "source" + (i + 1) + "_"; // 动态生成前缀:source1_, source2_等
|
|
|
+
|
|
|
+ // 为源文档样式添加带序号的前缀
|
|
|
+ prefixStyles(source, sourcePrefix);
|
|
|
+
|
|
|
+ // 合并样式表
|
|
|
+ mergeStyles(target, source, sourcePrefix);
|
|
|
+
|
|
|
+ // 合并内容
|
|
|
appendSourceContent(target, source);
|
|
|
}
|
|
|
-
|
|
|
try (FileOutputStream out = new FileOutputStream(outputPath)) {
|
|
|
target.write(out);
|
|
|
target.close();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private static void prefixStyles(XWPFDocument doc, String prefix) {
|
|
|
+ XWPFStyles styles = doc.getStyles();
|
|
|
+ if (styles == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建一个样式ID映射表
|
|
|
+ Map<String, String> styleIdMap = new HashMap<>();
|
|
|
+
|
|
|
+ // 处理所有样式
|
|
|
+ for (XWPFStyle style : styles.getStyles()) {
|
|
|
+ String oldId = style.getStyleId();
|
|
|
+ if (oldId != null && !oldId.startsWith(prefix)) {
|
|
|
+ String newId = prefix + oldId;
|
|
|
+ styleIdMap.put(oldId, newId);
|
|
|
+ style.setStyleId(newId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新文档中所有引用了样式的段落和表格
|
|
|
+ updateStyleReferences(doc, styleIdMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("checkstyle:NestedForDepth")
|
|
|
+ private static void updateStyleReferences(XWPFDocument doc, Map<String, String> styleIdMap) {
|
|
|
+ // 更新段落样式引用
|
|
|
+ for (XWPFParagraph p : doc.getParagraphs()) {
|
|
|
+ String styleId = p.getStyleID();
|
|
|
+ if (styleId != null && styleIdMap.containsKey(styleId)) {
|
|
|
+ p.setStyle(styleIdMap.get(styleId));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新表格样式引用
|
|
|
+ for (XWPFTable table : doc.getTables()) {
|
|
|
+ String styleId = table.getStyleID();
|
|
|
+ if (styleId != null && styleIdMap.containsKey(styleId)) {
|
|
|
+ table.setStyleID(styleIdMap.get(styleId));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新表格中的单元格样式
|
|
|
+ for (XWPFTableRow row : table.getRows()) {
|
|
|
+ for (XWPFTableCell cell : row.getTableCells()) {
|
|
|
+ for (XWPFParagraph p : cell.getParagraphs()) {
|
|
|
+ String cellStyleId = p.getStyleID();
|
|
|
+ if (cellStyleId != null && styleIdMap.containsKey(cellStyleId)) {
|
|
|
+ p.setStyle(styleIdMap.get(cellStyleId));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void mergeStyles(XWPFDocument target, XWPFDocument source, String prefix) {
|
|
|
+ XWPFStyles targetStyles = target.getStyles();
|
|
|
+ XWPFStyles sourceStyles = source.getStyles();
|
|
|
+ if (sourceStyles == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保目标文档有样式表
|
|
|
+ if (targetStyles == null) {
|
|
|
+ target.createStyles();
|
|
|
+ targetStyles = target.getStyles();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历源文档的所有样式
|
|
|
+ for (XWPFStyle sourceStyle : sourceStyles.getStyles()) {
|
|
|
+ // 获取原始样式ID和类型
|
|
|
+ String oldStyleId = sourceStyle.getStyleId();
|
|
|
+ STStyleType.Enum styleType = sourceStyle.getType();
|
|
|
+ if (oldStyleId == null || styleType == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成带前缀的新样式ID(如 "source1_1")
|
|
|
+ CTStyle copy = (CTStyle) sourceStyle.getCTStyle().copy();
|
|
|
+
|
|
|
+ // 1. 深拷贝源样式(包括所有XML属性)
|
|
|
+ XWPFStyle styleCopy = new XWPFStyle(copy, targetStyles);
|
|
|
+ styleCopy.setStyleId(oldStyleId); // 更新ID
|
|
|
+
|
|
|
+ // 2. 直接添加整个样式对象(利用自定义的addStyle方法)
|
|
|
+ targetStyles.addStyle(styleCopy); // 调用您提供的addStyle(XWPFStyle)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private static void appendSourceContent(XWPFDocument target, XWPFDocument source) throws InvalidFormatException {
|
|
|
for (IBodyElement element : source.getBodyElements()) {
|
|
|
if (element instanceof XWPFParagraph) {
|
|
@@ -56,11 +161,17 @@ public final class DocxUtil {
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
private static void appendTable(XWPFDocument target, XWPFTable oldTable) {
|
|
|
XWPFTable newTable = target.createTable();
|
|
|
newTable.getCTTbl().set(oldTable.getCTTbl().copy());
|
|
|
+ // 更新表格样式引用
|
|
|
+ String styleId = oldTable.getStyleID();
|
|
|
+ if (styleId != null) {
|
|
|
+ newTable.setStyleID(styleId);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private static void appendParagraph(XWPFDocument target, XWPFParagraph oldParagraph) throws InvalidFormatException {
|
|
@@ -96,6 +207,11 @@ public final class DocxUtil {
|
|
|
// Copy Xml content
|
|
|
XWPFParagraph newParagraph = target.createParagraph();
|
|
|
newParagraph.getCTP().set(oldParagraph.getCTP().copy());
|
|
|
+ // 更新段落样式引用
|
|
|
+ String styleId = oldParagraph.getStyleID();
|
|
|
+ if (styleId != null) {
|
|
|
+ newParagraph.setStyle(styleId);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
}
|