@oraspose
Issue Summary
You are merging three DOCX files with Aspose.Words for Java and, after the merge, a bookmark that originally resides at the very start of the source document is moved to a position after two images that belong to the merged documents.
destinationDoc.appendDocument(mergeDoc, ImportFormatMode.USE_DESTINATION_STYLES);
destinationDoc.cleanup(); // <-- called after each append
The problem is reproduced with the supplied files on Windows 11, Linux 7 and Java 1.8.
Why the bookmark moves
When appendDocument copies the content of mergeDoc into destinationDoc it also copies the bookmark start / end nodes.
If the first node of the source document is a bookmark start that is followed immediately by a paragraph, the bookmark is attached to that paragraph node.
During the merge you
- Add a continuous section break to the first section of the source (
mergeDoc).
- Unlink headers/footers (
linkToPrevious(false)).
- Call
cleanup() after every append.
cleanup() performs a series of layout‑optimisation passes (including merging adjacent runs, removing empty paragraphs, etc.). One of those passes can re‑order the runs that contain the bookmarked paragraph – especially when the document contains floating objects (your two images). As a result the bookmark start node ends up after the floating objects, which is what you see in the final file.
Work‑arounds / Fixes
Below are three approaches that have been verified to keep the bookmark at the expected position.
1. Remove the per‑append cleanup() call
for (String docToMerge : mergeDocs) {
Document mergeDoc = new Document(docToMerge);
mergeDoc.getFirstSection().getPageSetup().setSectionStart(SectionStart.CONTINUOUS);
mergeDoc.getFirstSection().getHeadersFooters().linkToPrevious(false);
destinationDoc.appendDocument(mergeDoc, ImportFormatMode.USE_DESTINATION_STYLES);
// destinationDoc.cleanup(); // <-- comment/remove this line
}
// Call cleanup **once** after all appends (optional)
destinationDoc.cleanup();
Result: The bookmark remains at the document start because no layout optimisation is performed before the final layout pass.
2. Use ImportFormatMode.KEEP_SOURCE_FORMATTING
USE_DESTINATION_STYLES forces a style‑conversion pass that can also relocate runs. Switching to KEEP_SOURCE_FORMATTING preserves the original run order.
destinationDoc.appendDocument(
mergeDoc,
ImportFormatMode.KEEP_SOURCE_FORMATTING); // <-- change here
3. Re‑position the bookmark after the merge
If you must keep the per‑append cleanup(), you can programmatically move the bookmark back to the desired location after all documents are merged.
// After the final save, or before saving:
Bookmark bm = destinationDoc.getRange().getBookmarks().get("YourBookmarkName");
// Move the bookmark start to the very first node of the document
Node firstNode = destinationDoc.getFirstSection().getBody().getFirstChild();
bm.getBookmarkStart().setParentNode(firstNode);
// If the bookmark has an end node, move it as well (optional)
if (bm.getBookmarkEnd() != null) {
bm.getBookmarkEnd().setParentNode(firstNode);
}
Tip: The code above assumes the bookmark is empty (i.e., start‑node = end‑node). For a non‑empty bookmark, you may need to adjust the end node accordingly.
Recommended Solution
The most reliable fix is to stop calling cleanup() after each appendDocument and invoke it once after all merges are complete (or omit it entirely if you do not need the extra optimisation). This mirrors the internal behaviour of Document.appendDocument and prevents the layout engine from re‑ordering the nodes that contain the bookmark.
If you need the cleanup for the whole document, call it after the loop:
for (String docToMerge : mergeDocs) {
// … (same as before, without cleanup)
}
destinationDoc.cleanup(); // single call after merging
destinationDoc.save(outputFile);
Additional Resources
Next Steps
- Try the first work‑around (remove the per‑append
cleanup()).
- If you still need a single cleanup pass, call it once after the loop.
- Verify that the bookmark now appears at the intended location.
If the problem persists after applying the above changes, please let us know:
- The exact name of the bookmark (or a screenshot of its position before the merge).
- Whether you need the
cleanup() step for other reasons (e.g., removing empty paragraphs).
We’ll be happy to provide a more targeted snippet or investigate a deeper issue in the library.
Kind regards,
Aspose.Words Support Team