问题描述:
当多个word文档进行合并的时候,每个文档都有书签的话,会出现书签丢失的问题?
原因:
通过对word转换成xml进行分析,发现多个文档中的 w:bookmarkStart <w:bookmarkStart w:id="0" w:name="bm__1613808247536"/><w:r><w:rPr><w:rFonts w:hint="eastAsia"/><w:b/><w:color w:val="FF0000"/></w:rPr><w:t>log文件</w:t></w:r><w:bookmarkEnd w:id="0"/> 书签标记中的 w:id的值 会出现重复。
解决思路:
找出每个文档中的书签,然后为每个书签标记w:id 设置唯一值即可解决。
实现代码:
package com.htdd.dmp.common.utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.htdd.common.utils.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WordUtils {
private static final Pattern pattern1 = Pattern.compile("<w:bookmarkStart w:id=\"\\d+\" w:name=\"\\w+\"/>");
private static final Pattern pattern2 = Pattern.compile("w:id=\"\\d+\"");
private static final int BOOKMARK_NUM = 100;// 每个word文档书签不能超过100
public static void main(String[] args) throws Exception {
List<String> list = Lists.newLinkedList();
list.add("E:\\ts\\tt\\17.docx");
list.add("E:\\ts\\tt\\18.docx");
list.add("E:\\ts\\tt\\19.docx");
list.add("E:\\ts\\tt\\20.docx");
list.add("E:\\ts\\tt\\21.docx");
list.add("E:\\ts\\tt\\22.docx");
list.add("E:\\ts\\tt\\23.docx");
String p = "E:\\ts\\tt\\ts.docx";
merge(list,p);
}
public static void merge(List<String> filePaths, String destPath) {
if(filePaths==null||filePaths.isEmpty()|| StringUtils.isEmpty(destPath)) {
System.out.println(">>>>>>filePaths和destPath都不能为空!!!");
return;
}
StringBuilder sb = new StringBuilder();
XmlOptions optionOuter = new XmlOptions();
optionOuter.setSaveOuter();
AtomicInteger ai = new AtomicInteger(100);
try(InputStream is = new FileInputStream(filePaths.get(0));XWPFDocument xwpf = new XWPFDocument(is)) {
CTBody firstBody = xwpf.getDocument().getBody();
Map<String,String> map = getContentMap(firstBody);
String header = map.get("head");
sb.append(header);
String docStr = map.get("body");
sb.append(dealBookMark(docStr,ai));
String tail = map.get("tail");
CTBody body;
for (int i = 1; i < filePaths.size(); i++) {
String path = filePaths.get(i);
try(InputStream is2 = new FileInputStream(path);XWPFDocument xwpf2 = new XWPFDocument(is2)) {
body = xwpf2.getDocument().getBody();
docStr = getBody(body,optionOuter);
ai.addAndGet(BOOKMARK_NUM*i);
sb.append(dealBookMark(docStr,ai));
}
}
try (OutputStream dest = new FileOutputStream(destPath)) {
CTBody makeBody = CTBody.Factory.parse(sb.append(tail).toString());
firstBody.set(makeBody);
xwpf.write(dest);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException | XmlException e) {
e.printStackTrace();
}
}
private static String getBody(CTBody src, XmlOptions optionsOuter) {
String appendString = src.xmlText(optionsOuter);
return appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
}
private static Map<String, String> getContentMap(CTBody body){
String srcString = body.xmlText();
Map<String, String> map = Maps.newHashMap();
String head = srcString.substring(0, srcString.indexOf(">") + 1);
map.put("head", head);
String mainPart = srcString.substring(srcString.indexOf(">") + 1, srcString.lastIndexOf("<"));
map.put("body",mainPart);
String tail = srcString.substring(srcString.lastIndexOf("<"));
map.put("tail",tail);
return map;
}
private static String dealBookMark(String docStr, AtomicInteger ai) {
Matcher matcher = pattern1.matcher(docStr);
Matcher m;
while (matcher.find()) {
String matchStr = matcher.group();
m = pattern2.matcher(matchStr);
if (m.find()) {
String idStr = m.group();
String idReplaceStr = "w:id=\"" + ai.getAndAdd(1) + "\"";
docStr = docStr.replaceFirst(idStr,idReplaceStr).replaceFirst(idStr,idReplaceStr);
}
}
return docStr;
}
}