问题:指定书签位置替换,插入文本信息,会删除不属于书签所在内容
原因:获取书签位置段落,存在书签只是段落其中的一个或者多个run,如果移除段落中的run,则会删除多余内容,无法达到预期效果
解决方案:
1.找书签位置时,找到书签开始至结束中所有的run区域的node节点集合
1.1 找到document的body以及页眉页脚中所有的段落集合
1.2 遍历段落信息,以段落的node节点开始,查找本个段落包含书签的node集合,把书签名字和段落信息以及处于书签内的node节点集合映射起来。
public static Map<String, WordParagraphNodesDTO> getAllDocumentBookmarkNameCustom(XWPFDocument document) {
// 创建返回书签映射段落node节点集合
Map<String, WordParagraphNodesDTO> bookmarkParagraphMap = new HashMap<>();
// 获取doc文档body内容中段落集合书签段落映射集合信息
WordBookmarkUtils.getParagraphsBookmark(document.getParagraphs(), bookmarkParagraphMap);
// 获取页脚集合 并且遍历页脚
List<XWPFFooter> footerList = document.getFooterList();
for (XWPFFooter footer : footerList) {
// 获取页脚所有表格中书签信息
getTablesBookmark(footer.getTables(),bookmarkParagraphMap);
// 获取页脚中所有段落的书签信息
getParagraphsBookmark(footer.getParagraphs(), bookmarkParagraphMap);
}
// 获取页眉集合 并且遍历页眉
List<XWPFHeader> headerList = document.getHeaderList();
for (XWPFHeader header : headerList) {
// 获取页眉中所有表格的书签信息
getTablesBookmark(header.getTables(),bookmarkParagraphMap);
// 获取页眉中所有段落的书签信息
getParagraphsBookmark(header.getParagraphs(), bookmarkParagraphMap);
}
// 获取docbody中所有表格集合书签段落映射集合信息
getTablesBookmark(document.getTables(),bookmarkParagraphMap);
// 返回映射集合
return bookmarkParagraphMap;
}
// 获取段落映射信息
private static void getParagraphsBookmark(List<XWPFParagraph> paragraphs, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {
if (CollectionUtils.isEmpty(paragraphs)){
return;
}
// 遍历段落
for (XWPFParagraph paragraph : paragraphs) {
// 获取,递归获取
getBookmarkParagraphRecursion(paragraph.getCTP().getDomNode(),paragraph,bookmarkParagraphMap);
}
}
// 递归获取当前段落中所有的书签段落映射信息
public static void getBookmarkParagraphRecursion(Node node, XWPFParagraph paragraph, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {
if (Objects.equals("w:bookmarkStart", node.getNodeName())) {
String nodeNameValue = node.getAttributes().getNamedItem("w:name").getNodeValue();
String nodeIdValue = node.getAttributes().getNamedItem("w:id").getNodeValue();
WordParagraphNodesDTO wordParagraphNodesDTO = new WordParagraphNodesDTO();
wordParagraphNodesDTO.setParagraph(paragraph);
ArrayList<Node> bookmarkNodeList = new ArrayList<>();
bookmarkNodeList.add(node);
wordParagraphNodesDTO.setBookmarkNodeList(bookmarkNodeList);
while (null != node.getNextSibling()){
node = node.getNextSibling();
bookmarkNodeList.add(node);
if (Objects.equals("w:bookmarkEnd", node.getNodeName()) && Objects.equals(nodeIdValue, node.getAttributes().getNamedItem("w:id").getNodeValue())){
bookmarkNodeList.add(node);
break;
}
}
bookmarkParagraphMap.put(nodeNameValue, wordParagraphNodesDTO);
} else {
NodeList childNodes = node.getChildNodes();
for (int i = 0, j = childNodes.getLength(); i < j; i++) {
Node childNode = childNodes.item(i);
getBookmarkParagraphRecursion(childNode, paragraph, bookmarkParagraphMap);
}
}
}
// 遍历段落 获取映射信息
private static void getTablesBookmark( List<XWPFTable> tables , Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {
if (CollectionUtils.isEmpty(tables)){
return;
}
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> tableCells = row.getTableCells();
for (XWPFTableCell cell : tableCells) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
getBookmarkParagraphRecursion(paragraph.getCTP().getDomNode(),paragraph,bookmarkParagraphMap);
}
}
}
}
}
2.插入数据时,先将本段落处于书签的run区域集合全部替换成空字符串,再将指定内容插入到当前的书签中
2.1 根据书签名称获取到映射到的段落,以及处于书签内的node集合
2.2 将处于书签内的node节点内容全部替换成空字符串
2.3 在书签存在的第一个node节点的run区域插入想要的文本内容(文本,图片)
WordParagraphNodesDTO wordParagraphNode = bookmarkNameParagraphMap.get(bookName);
if (null == wordParagraphNode){
return;
}
XWPFParagraph paragraph = wordParagraphNode.getParagraph();
List<XWPFRun> runList = paragraph.getRuns();
XWPFRun replaceRun = null;
if (CollectionUtils.isNotEmpty(runList)){
for (XWPFRun xwpfRun : runList) {
if (wordParagraphNode.getBookmarkNodeList().contains(xwpfRun.getCTR().getDomNode())){
replaceRun = xwpfRun;
int sizeOfTArray = xwpfRun.getCTR().sizeOfTArray();
for (int i = 0; i < sizeOfTArray; i++) {
xwpfRun.setText("",i);
}
}
}
}
// 插入内容
replaceRun.setText("内容");
2023-12-11新增:
出现出现书签仅仅为一个光标,没有区域,观察xml如下,无法根据node准确定位到当前段落的哪一个位置,需要获取当前段落中匹配的node节点的上一个节点,根据上个节点定位到书签位置,插入指定信息。
获取位置添加以下代码:
wordParagraphNodesDTO.setPreNode(node.getPreviousSibling());
插入位置添加一下代码:定位插入书签位置的前一个run区域,插入数据
if (null == replaceRun){
for (XWPFRun xwpfRun : runList) {
if (Objects.equals(xwpfRun.getCTR().getDomNode(),wordParagraphNode.getPreNode())){
replaceRun = xwpfRun;
break;
}
}
}
2024-4-22 新增:书签跨段落,无法删除书签中内容
public static void getBookmarkParagraphRecursion(Node node, XWPFParagraph paragraph, Map<String, WordParagraphNodesDTO> bookmarkParagraphMap) {
if (Objects.equals(BOOKMARK_START_TAG, node.getNodeName())) {
String nodeNameValue = node.getAttributes().getNamedItem("w:name").getNodeValue();
if (nodeNameValue.startsWith("_")){
return;
}
String nodeIdValue = node.getAttributes().getNamedItem("w:id").getNodeValue();
WordParagraphNodesDTO wordParagraphNodesDTO = new WordParagraphNodesDTO();
wordParagraphNodesDTO.setParagraph(paragraph);
ArrayList<Node> bookmarkNodeList = new ArrayList<>();
bookmarkNodeList.add(node);
wordParagraphNodesDTO.setPreNode(node.getPreviousSibling());
wordParagraphNodesDTO.setBookmarkNodeList(bookmarkNodeList);
boolean bookmarkEndFlag = false; // 书签结束标记,书签跨段落
// 一直循环匹配书签id 的结束节点 todo 可能出现书签和段落平级问题,目前还未解决
while (null != node.getNextSibling()){
node = node.getNextSibling();
bookmarkNodeList.add(node);
if (Objects.equals(BOOKMARK_END_TAG, node.getNodeName()) && Objects.equals(nodeIdValue, node.getAttributes().getNamedItem("w:id").getNodeValue())){
bookmarkNodeList.add(node);
bookmarkEndFlag = true;
break;
}
}
// 段落集合
List<XWPFParagraph> xwpfParagraphList = new ArrayList<>();
if (!bookmarkEndFlag){
XWPFDocument document = paragraph.getDocument();
boolean endFlag = false;
for (int i = document.getPosOfParagraph(paragraph) - 5; i <document.getParagraphs().size(); i++) {
XWPFParagraph xwpfParagraph = document.getParagraphArray(i);
if (xwpfParagraph != null) {
xwpfParagraphList.add(xwpfParagraph);
CTMarkupRange[] bookmarkEndArray = xwpfParagraph.getCTP().getBookmarkEndArray();
for (CTMarkupRange ctMarkupRange : bookmarkEndArray) {
String nodeId = ctMarkupRange.getDomNode().getAttributes().getNamedItem("w:id").getNodeValue();
if (Objects.equals(nodeId, nodeIdValue)) {
endFlag = true;
break;
}
}
}
if (endFlag){
wordParagraphNodesDTO.setParagraphList(xwpfParagraphList);
break;
}
}
}
bookmarkParagraphMap.put(nodeNameValue, wordParagraphNodesDTO);
} else {
NodeList childNodes = node.getChildNodes();
for (int i = 0, j = childNodes.getLength(); i < j; i++) {
Node childNode = childNodes.item(i);
// 递归
getBookmarkParagraphRecursion(childNode, paragraph, bookmarkParagraphMap);
}
}
}