@sfulari
Why extra bookmarks appear after Aspose.Words generation?
When a .doc/.docx file is loaded or saved by Aspose.Words, the library preserves all bookmark nodes that exist in the Word package – including the hidden system bookmarks that are automatically created by Microsoft Word itself.
Typical hidden bookmarks that you will see in the Bookmarks collection are:
| Hidden bookmark name |
When it is created |
_GoBack |
Created by Word for the Go‑Back command. |
_Footnote / _Endnote |
Added when a footnote/endnote exists. |
_Toc* |
Generated for a Table‑of‑Contents field. |
_Picture* |
Added for inline pictures, charts, etc. |
_Hyperlink* |
Produced when a hyperlink is inserted. |
_ContentControl* |
For structured‑document‑tags (SDT). |
These bookmarks are not visible in the Word UI (they are marked as hidden), but they are part of the OpenXML package. Aspose.Words faithfully loads them and, unless you explicitly remove them, they are written back when the document is saved. This is why a template that originally contains one user‑defined bookmark can end up with 8, 10, … bookmarks after processing.
Is this behaviour expected?
Yes. Aspose.Words does not create any additional bookmarks on its own; it merely preserves the ones that already exist in the source document – including the hidden ones described above. This is the default and documented behaviour of the library.
Reference: Aspose.Words – Working with Bookmarks
How to keep only the original (user‑defined) bookmarks?
There are two common strategies:
- Remove unwanted bookmarks after the document is generated.
- Prevent hidden bookmarks from being added in the first place (when possible).
Below are code samples for both approaches.
1. Removing unwanted bookmarks after generation
using Aspose.Words;
// Load the template
Document doc = new Document("Template.docx");
// *** Your document generation logic here ***
// e.g. mail‑merge, DocumentBuilder, etc.
// -----------------------------------------------------------
// 1️⃣ Capture the list of *known* (user‑defined) bookmark names
HashSet<string> wanted = new HashSet<string>();
foreach (Bookmark bm in doc.Range.Bookmarks)
{
// Keep only bookmarks that do NOT start with an underscore (_)
// – most hidden bookmarks use this convention.
if (!bm.Name.StartsWith("_"))
wanted.Add(bm.Name);
}
// -----------------------------------------------------------
// 2️⃣ Remove every bookmark that is NOT in the wanted list
for (int i = doc.Range.Bookmarks.Count - 1; i >= 0; i--)
{
Bookmark bm = doc.Range.Bookmarks[i];
if (!wanted.Contains(bm.Name))
{
// Remove the bookmark node pair (Start/End)
bm.Remove();
}
}
// -----------------------------------------------------------
// 3️⃣ Save the final document
doc.Save("Result.docx");
Why iterate backwards?
Bookmarks.Remove() modifies the collection, so iterating from the end prevents index‑out‑of‑range exceptions.
Tip: If you need to keep all user‑defined bookmarks regardless of their naming pattern, record the names before you start any generation work (e.g., right after loading the template) and use that list for the filter.
2. Suppressing hidden bookmarks during generation (when possible)
| Scenario |
What you can do |
| Inserting a Table‑of‑Contents |
Use doc.UpdateFields() after you have finished editing. The TOC creates its own hidden bookmarks (_Toc*). If you don’t need a TOC, simply skip the InsertTableOfContents call. |
| Inserting Hyperlinks |
Prefer DocumentBuilder.InsertHyperlink(string text, string url, bool isUrlAbsolute) – this does not generate a hidden bookmark unless you explicitly request a bookmark target. |
| Adding Pictures/Charts |
Insert images via DocumentBuilder.InsertImage(...). The library does not create hidden bookmarks for the image itself. |
| Mail‑Merge |
Use MailMerge.Execute(... ) without the Options.UseLegacyMergeFieldProcessing flag – the legacy mode may add temporary bookmarks that are later removed automatically, but the modern mode keeps the source structure intact. |
If you cannot avoid a specific operation that creates hidden bookmarks (e.g., a TOC is required), the post‑generation clean‑up shown in section 1 remains the reliable way to keep only the bookmarks you care about.
Quick checklist for your project
 |
Action |
| 1 |
After loading the template, store the list of user‑defined bookmark names (doc.Range.Bookmarks). |
| 2 |
Run all your generation code (mail‑merge, DocumentBuilder, etc.). |
| 3 |
Iterate backwards through doc.Range.Bookmarks and remove every bookmark whose name is not in the stored list. |
| 4 |
Save the document. |
| 5 |
(Optional) If you notice a particular operation always adds a specific hidden bookmark, consider adjusting the code as described in the table above. |
References
If you need a concrete example tailored to the exact generation steps you are using (e.g., mail‑merge, InsertHtml, etc.), feel free to share that snippet and I’ll provide a focused code sample. Happy coding!