Insert BuildingBlock right after a Bookmark or MergeField

We’ve been evaluating various java based solutions for manipulating existing word files, in order to fullfill our customer’s requirements to build a very complex java-web-based word reporting solution. Aspose.Word For Java seems to fit like a glove to most of our requirements. Unfortunately we haven’t been able yet to accomplish the following (attached) use-case.

We need to find certain BuilingBlock’s within the .dotx template and insert the BuildingBlock at a exact position (marked with a BookMark or MergeField) within the destination document.

I attached a solution (BuildingBlockExample.java), which was mainly inspired by the following wiki entry:
https://docs.aspose.com/words/java/insert-and-append-documents/

If you compare the generated ouput by aspose called “Example001.docx” with the expected document “Example001-Expected.docx” you’ll see that we haven’t been able to insert the BuilingBlocks right after the Bookmark or MergeField.
We’ve also been trying to understand the following Line / Method:

dstStory.insertAfter(newNode, insertAfterNode);

but didn’t find a lot of documentation.

Java Code:

public class BuildingBlockExample {

    public static void main(String args[]) throws Exception {

        // loadLicense();
        Document document = new Document("C:/temp/aspose/Example001.dotx");
        BuildingBlock buildingBlock = findBuildingBlockByName(document, "BuildingBlock001");

        DocumentBuilder builder = new DocumentBuilder(document);
        builder.moveToBookmark("Bookmark1");
        insertBuildingBlock(builder.getCurrentParagraph(), buildingBlock);

        buildingBlock = findBuildingBlockByName(document, "BuildingBlock002");
        builder.moveToMergeField("Field1");
        insertBuildingBlock(builder.getCurrentParagraph(), buildingBlock);
        document.save("C:/temp/aspose/Example001.docx");

    }

    private static void insertBuildingBlock(Node insertAfterNode, BuildingBlock bb) {
        // Make sure that the node is either a paragraph or table.
        if ((insertAfterNode.getNodeType() != NodeType.PARAGRAPH) & (insertAfterNode.getNodeType() != NodeType.TABLE)) {
            throw new IllegalArgumentException("The destination node should be either a paragraph or table.");
        }

        Node origNode = insertAfterNode;
        // We will be inserting into the parent of the destination paragraph.
        CompositeNode<?> dstStory = insertAfterNode.getParentNode();

        // This object will bebb. translating styles and lists during the import.
        NodeImporter importer = new NodeImporter(bb.getDocument(), insertAfterNode.getDocument(), ImportFormatMode.KEEP_SOURCE_FORMATTING);

        // Loop through all sections in the source document.
        for (Section srcSection : bb.getSections()) {
            // Loop through all block level nodes (paragraphs and tables) in the body of the section.
            for (Node srcNode : (Iterable) srcSection.getBody()) {
                // Let’s skip the node if it is a last empty paragraph in a section
                if (srcNode.getNodeType() == (NodeType.PARAGRAPH)) {
                    Paragraph para = (Paragraph) srcNode;
                    if (para.isEndOfSection() && !para.hasChildNodes()) {
                        continue;
                    }
                }

                // This creates a clone of the node, suitable for insertion into the destination document.
                Node newNode = importer.importNode(srcNode, true);

            // Insert new node after the reference node.
                dstStory.insertAfter(newNode, insertAfterNode);
                insertAfterNode = newNode;
            }
        }
    }

    private static BuildingBlock findBuildingBlockByName(Document document, String buildingBlockName) {
        Iterator it = document.getGlossaryDocument().getBuildingBlocks().iterator();
        while (it.hasNext()) {
            BuildingBlock bb = it.next();
            if (bb.getName().equals(buildingBlockName)) {
                return bb;
            }
        }
        return null;
    }
}

still struggling around to solve this problem :confused:

Hi
Patrick,

Thanks for the inquiry and sorry for delayed response.

First of all please note that a table is represented as block level node in Aspose.Words DOM whereas Bookmark is represented as in-line level node. so a Bookmark can’t contain a Table as its Child Node. For more details, please visit documentation below:
https://reference.aspose.com/words/net/aspose.words/node/
https://docs.aspose.com/words/net/aspose-words-document-object-model/

Moreover, you can try using the technique described here to insert contents at a specific bookmark:
https://forum.aspose.com/t/87283

The provided code is in C#, but I think there will not be problems to translate it to Java.

Please let us know if you need more information, we are always glad to help you.

Thanks for you response. I used to the described technique in the mentioned Post, but had some problems (weird output). After searching around in the forum I found a bunch of similar techniques (mainly posted by the User: alexey.noskov) describing the exact same problem as I posted. I tried various techniques and ended up with the following solution (which seems to be the most recent on I’ve found):
https://forum.aspose.com/t/63485

This solutions seems to work fine for us, for the documents we tested it against. The only concerns I have is, that the technique looks very complex and it’s hard to understand fully. And since it is a pretty common usage, it might by worth it to let aspose.word providing this technique either within the wiki, or as a function within the code. Within each post I found the technique, something has been added or changed in order to fix a bug within the algorithm or the enhance it. Thus I’d really appreciate if this algorithm is maintained somewhere in aspose (wiki or within the code). From a customer point of view, I’d be able to get the most recent algorithm in every release (if it is directly in the code) or as soon as I’m having a problem within that algorithm I’d be able to look the most recent one up within the wiki.

However thanks again for your code.

Here is our final code after we migrated it from mentioned post from C# to Java:

private void insertBuildingBlockAtBookmark(String bookmarkName, Document mainDocument, BuildingBlock buildingBlock) throws Exception {
    NodeImporter importer = new NodeImporter(buildingBlock.getDocument(), mainDocument, ImportFormatMode.KEEP_SOURCE_FORMATTING);

    // Create DocumentBuilder
    DocumentBuilder builder = new DocumentBuilder(mainDocument);

    // Move cursor to bookmark and insert paragraph break
    builder.moveToBookmark(bookmarkName);
    builder.writeln();

    // If current paragraph is a list item, we should clear its formating.
    if (builder.getCurrentParagraph().isListItem()) {
        builder.getListFormat().removeNumbers();
    }

    // Content of srcdoc will be inserted after this node
    Node insertAfterNode = builder.getCurrentParagraph().getPreviousSibling();

    // Content of first paragraph of srcDoc will be apended to this parafraph
    Paragraph insertAfterParagraph = null;
    if (insertAfterNode.getNodeType() == NodeType.PARAGRAPH) {
        insertAfterParagraph = (Paragraph) insertAfterNode;
    }

    // Content of last paragraph of srcDoc will be apended to this parafraph
    Paragraph insertBeforeParagraph = builder.getCurrentParagraph();

    // We will be inserting into the parent of the destination paragraph.
    CompositeNode dstStory = insertAfterNode.getParentNode();

    // Remove empty paragraphs from the end of document
    while (!((CompositeNode) buildingBlock.getLastSection().getBody().getLastChild()).hasChildNodes()) {
        buildingBlock.getLastSection().getBody().getLastParagraph().remove();
        if (buildingBlock.getLastSection().getBody().getLastChild() == null) {
            break;
        }
    }

    // Loop through all sections in the source document.
    for (Section srcSection : buildingBlock.getSections()) {
        // Loop through all block level nodes (paragraphs and tables) in the body of the section.
        for (int nodeIdx = 0; nodeIdx < srcSection.getBody().getChildNodes().getCount(); nodeIdx++) {

            Node srcNode = srcSection.getBody().getChildNodes().get(nodeIdx);

            // Do not insert node if it is a last empty paragarph in the section.
            Paragraph para = null;
            if (srcNode.getNodeType() == NodeType.PARAGRAPH) {
                para = (Paragraph) srcNode;
            }

            if ((para != null) && para.isEndOfSection() && (!para.hasChildNodes())) {
                break;
            }

            // If current paragraph is first paragraph of srcDoc
            // then appent its content to insertAfterParagraph
            boolean nodeInserted = false;
            if (para != null && para.equals(buildingBlock.getFirstSection().getBody().getFirstChild())) {
                nodeInserted = true; // set this flag to know that we already processed this node.
                // !String.IsNullOrEmpty(insertAfterParagraph.toTxt().trim()

                if (!(insertAfterParagraph.toTxt() == null || insertAfterParagraph.toTxt().trim().equals(""))) {
                    for (int i = 0; i < para.getChildNodes().getCount(); i++) {
                        Node node = para.getChildNodes().get(i);
                        Node dstNode = importer.importNode(node, true);
                        insertAfterParagraph.appendChild(dstNode);
                    }
                } else {
                    Paragraph dstNode = (Paragraph) mainDocument.importNode(para, true);
                    insertAfterParagraph.getParentNode().insertAfter(dstNode, insertAfterParagraph);
                    while (insertAfterParagraph.hasChildNodes()) {
                        dstNode.prependChild(insertAfterParagraph.getLastChild());
                    }
                    insertAfterParagraph.remove();
                    insertAfterParagraph = dstNode;
                    insertAfterNode = dstNode;
                }

                // If subdocument contains only one paragraph
                // then copy content of insertBeforeParagraph to insertAfterParagraph
                // and remove insertBeforeParagraph
                if (buildingBlock.getFirstSection().getBody().getFirstParagraph()
                        .equals(buildingBlock.getLastSection().getBody().getLastChild())) {
                    while (insertBeforeParagraph.hasChildNodes()) {
                        insertAfterParagraph.appendChild(insertBeforeParagraph.getFirstChild());
                    }
                    insertBeforeParagraph.remove();
                }
            }
            // Fix: Avoid if the current paragraph is the first and last paragraph
            else
                // If current paragraph is last paragraph of srcDoc
                // then appent its content to insertBeforeParagraph
                if (para != null && para.equals(buildingBlock.getLastSection().getBody().getLastChild())) {
                    nodeInserted = true; // set this flag to know that we already processed this node.
                    Paragraph dstNode = (Paragraph) mainDocument.importNode(para, true);
                    insertBeforeParagraph.getParentNode().insertBefore(dstNode, insertBeforeParagraph);
                    while (insertBeforeParagraph.hasChildNodes()) {
                        dstNode.appendChild(insertBeforeParagraph.getFirstChild());
                    }
                    insertBeforeParagraph.remove();
                    insertBeforeParagraph = dstNode;
                }

            if (!nodeInserted) {
                // This creates a clone of the node, suitable for insertion into the destination document.
                Node newNode = mainDocument.importNode(srcNode, true);
                // Insert new node after the reference node.
                dstStory.insertAfter(newNode, insertAfterNode);
                insertAfterNode = newNode;
            }
        }
    }
}

Hi Patrick,

Thanks for your inquiry.

It’s great you were able to find the code you need. I have added your request to include a one line InsertDocument member in the Aspose.Words API that would handle this use case. This should make what your doing much easier (as you can copy the building block into a new document and insert it at a bookmark). We will keep you informed of any developments.

Regarding adding this code the wiki, that is a good idea, however I have been reluctant to do so because of the length and complexity of the code. Most of the time users only need to insert documents at block level and not at inline level. I will consider adding this method to the documentation.

Thanks,

The issues you have found earlier (filed as WORDSNET-5251) have been fixed in this .NET update and this Java update.

This message was posted using Notification2Forum from Downloads module by aspose.notifier.
(14)