首页 > 解决方案 > 如何在不丢失格式的情况下使用 POI 替换“.docx”中的书签?

问题描述

我正在尝试用值替换书签。

private FileInputStream fis = new FileInputStream(new File("D:\\test.docx"));
private XWPFDocument document = new XWPFDocument(fis);
List<XWPFParagraph> paraList = this.document.getParagraphs();

private final void procParaList(List<XWPFParagraph> paraList, String bookmarkName, String bookmarkValue) {
    Iterator<XWPFParagraph> paraIter = null;
    XWPFParagraph para = null;
    List<CTBookmark> bookmarkList = null;
    Iterator<CTBookmark> bookmarkIter = null;
    CTBookmark bookmark = null;
    XWPFRun run = null;
    Node nextNode = null;

    paraIter = paraList.iterator();
    while (paraIter.hasNext()) {
        para = paraIter.next();
        bookmarkList = para.getCTP().getBookmarkStartList();
        bookmarkIter = bookmarkList.iterator();

        while (bookmarkIter.hasNext()) {
            bookmark = bookmarkIter.next();
            if (bookmark.getName().equals(bookmarkName)) {
                run = para.createRun();
                run.setText(bookmarkValue);
                nextNode = bookmark.getDomNode().getNextSibling();
                while (!(nextNode.getNodeName().contains("bookmarkEnd"))) {
                    para.getCTP().getDomNode().removeChild(nextNode);
                    nextNode = bookmark.getDomNode().getNextSibling();
                }
                para.getCTP().getDomNode().insertBefore(run.getCTR().getDomNode(), nextNode);
            }
        }
    }
}

我可以将书签替换为值,但它与书签文本的格式(字体系列、字体大小、颜色等)不同。

任何人都可以提供一些建议。

标签: javaapache-poi

解决方案


如前所述,我相信这是您的确切用例,官方存档链接 帮助 请重点关注使用Node styleNode复制样式信息。

    /**
     * Replace the text - if any - contained between the bookmarkStart and
it's
     * matching bookmarkEnd tag with the text specified. The technique used
will
     * resemble that employed when inserting text after the bookmark. In
short,
     * the code will iterate along the nodes until it encounters a matching
     * bookmarkEnd tag. Each node encountered will be deleted unless it is
the
     * final node before the bookmarkEnd tag is encountered and it is a
     * character run. If this is the case, then it can simply be updated to
     * contain the text the users wishes to see inserted into the document.
If
     * the last node is not a character run, then it will be deleted, a new
run
     * will be created and inserted into the paragraph between the
bookmarkStart
     * and bookmarkEnd tags.
     *
     * @param run An instance of the XWPFRun class that encapsulates the
text
     * that is to be inserted into the document following the bookmark.
     */
    private void replaceBookmark(XWPFRun run) {
        Node nextNode = null;
        Node styleNode = null;
        Node lastRunNode = null;
        Node toDelete = null;
        NodeList childNodes = null;
        Stack<Node> nodeStack = null;
        boolean textNodeFound = false;
        boolean foundNested = true;
        int bookmarkStartID = 0;
        int bookmarkEndID = -1;
        int numChildNodes = 0;

        nodeStack = new Stack<Node>();
        bookmarkStartID = this._ctBookmark.getId().intValue();
        nextNode = this._ctBookmark.getDomNode();
        nodeStack.push(nextNode);

        // Loop through the nodes looking for a matching bookmarkEnd tag
        while (bookmarkStartID != bookmarkEndID) {
            nextNode = nextNode.getNextSibling();
            nodeStack.push(nextNode);

            // If an end tag is found, does it match the start tag? If so,
end
            // the while loop.
            if (nextNode.getNodeName().contains(Bookmark.BOOKMARK_END_TAG))
{
                try {
                    bookmarkEndID = Integer.parseInt(
                            nextNode.getAttributes().getNamedItem(
                            Bookmark.BOOKMARK_ID_ATTR_NAME).getNodeValue());
                } catch (NumberFormatException nfe) {
                    bookmarkEndID = bookmarkStartID;
                }
            }
            //else {
                // Place a reference to the node on the nodeStack
            //    nodeStack.push(nextNode);
            //}
        }

        // If the stack of nodes found between the bookmark tags is not
empty
        // then they have to be removed.
        if (!nodeStack.isEmpty()) {

            // Check the node at the top of the stack. If it is a run, get
it's
            // style - if any - and apply to the run that will be replacing
it.
            //lastRunNode = nodeStack.pop();
            lastRunNode = nodeStack.peek();
            if ((lastRunNode.getNodeName().equals(Bookmark.RUN_NODE_NAME)))
{
                styleNode = this.getStyleNode(lastRunNode);
                if (styleNode != null) {
                    run.getCTR().getDomNode().insertBefore(
                            styleNode.cloneNode(true),
run.getCTR().getDomNode().getFirstChild());
                }
            }

推荐阅读