Bookmarking while Finding and Replacing

I want to bookmark each and every occurence of a specific pattern for further processing. The following ReplaceAction function gives the closest result I can get:

private ReplaceAction BookmarkTokens(object sender, ReplaceEvaluatorArgs e)
DocumentBuilder builder = new DocumentBuilder(doc);
string bkname = "bk" + bkindex.ToString();
return ReplaceAction.Skip;

It has two problems I cannot seem to overcome.

First, the EndBookmark is equal to the StartBookmark, which is wrong. How can I indicate the DocumentBuilder to move to the length of the match value prior from setting the EndBookmark?

Second, if a paragraph contains more than one pattern, how can I indicate the DocumentBuilder to move to the offset of the match prior from setting the StartBookmark?



The task is not that trivial actually. The matching string may be spread over several runs and not aligned to the beginning of the first run and the end of the last run. We should be ready to split the first and the lust runs and insert BookmarkStart and BookmarkEnd nodes at the split points.

Here's a sample solution for your task. Note it may not work for some special cases but it does work with a simple test document I've made. Hope it gives you some clues and you will be able to adjust it if needed.


public void TestReplace()


Document doc = new Document(@"D:\Work\Bookmarks.doc");

doc.Range.Replace(new Regex("Pattern"), new ReplaceEvaluator(ReplaceEvaluator), false);

doc.Save(@"D:\Work\Bookmarks Out.doc");


private ReplaceAction ReplaceEvaluator(object sender, ReplaceEvaluatorArgs args)



Run startRun = (Run)args.MatchNode;

Document doc = startRun.Document;

startRun = SplitRun(startRun, args.MatchOffset, new BookmarkStart(doc, "My bookmark " + mBookmarkIndex));

Run run = startRun;

Run lastRun = null;

int curLength = 0;

while ((run != null) && (curLength < args.Match.Value.Length))


curLength += run.Text.Length;

lastRun = run;

run = (Run)run.NextSibling;


int lastOffset = lastRun.Text.Length - (curLength - args.Match.Value.Length);

SplitRun(lastRun, lastOffset, new BookmarkEnd(doc, "My bookmark " + mBookmarkIndex));

return ReplaceAction.Skip;


private Run SplitRun(Run run, int offset, Node nodeToInsert)


Paragraph parentParagraph = run.ParentParagraph;

Run run1 = (Run)run.Clone(false);

run1.Text = run.Text.Substring(0, offset);

Run run2 = (Run)run.Clone(false);

run2.Text = run.Text.Substring(offset);

parentParagraph.InsertAfter(run1, run);

parentParagraph.InsertAfter(nodeToInsert, run1);

parentParagraph.InsertAfter(run2, nodeToInsert);


return run2;