Find and Bookmark Keyword Tags in Word Document | Create Cross References from Text Placeholders | Insert Number Hyperlink Field C# .NET

We need to programatically create cross references in a word document from text placeholders within the same document. One idea I have is to do a search and replace of the text placeholders and replace them with bookmarks. Then create the cross reference from the bookmarks. I have successfully done the second part by manually inserting bookmarks into the document and then I have a function that cross references the bookmarks.

The part I cannot get working is the function that searches for a text in a special format (ie @ref_myreference@ - (in this case a regex that would search for text beginning with @ref_ and ending in @) - once it finds it, it will create a bookmark at that location calling it the same name as the tag and then remove the text tag.

Thanks

@caseflow,

Please ZIP and attach the following resources here for testing:

  • Your simplified input Word document
  • Your expected document (DOCX file) showing the desired output. You can create this document by using MS Word and attach it here for our reference

As soon as you get these pieces of information ready, we will start investigation into your scenario and provide you code to achieve the same by using Aspose.Words. Thanks for your cooperation.

Hi Awais, sample before and after documents attachedAspose-xref.zip (52.2 KB)

@caseflow,

I think, you can build logic on the following code to get the desired output. Hope, this helps.

Document doc = new Document("E:\\Temp\\Aspose-xref\\Aspose-xref-before.docx");
doc.UpdateListLabels();

FindReplaceOptions options = new FindReplaceOptions()
{
    Direction = FindReplaceDirection.Backward,
    PreserveMetaCharacters = true,
    ReplacingCallback = new FindAndReplace1()
};
doc.Range.Replace(new Regex("@ref_.+?@"), "", options);

doc.Save("E:\\Temp\\Aspose-xref\\20.2.docx"); 

private static Run SplitRun(Run run, int position)
{
    Run afterRun = (Run)run.Clone(true);
    afterRun.Text = run.Text.Substring(position);
    run.Text = run.Text.Substring((0), (0) + (position));
    run.ParentNode.InsertAfter(afterRun, run);
    return afterRun;
}

private class FindAndReplace1 : IReplacingCallback
{
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        Node currentNode = e.MatchNode;

        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        ArrayList runs = new ArrayList();

        int remainingLength = e.Match.Value.Length;
        while (
            (remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }

        // Encapsulate matched string inside a Bookmark
        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
        builder.MoveTo((Run)runs[0]);

        builder.StartBookmark("_bm_" + e.Match.Value.Replace("@", ""));
        BookmarkEnd bmEnd = builder.EndBookmark("_bm_" + e.Match.Value.Replace("@", ""));

        Run lastRun = (Run)runs[runs.Count - 1];
        lastRun.ParentNode.InsertAfter(bmEnd, lastRun);

        // Now search again document for all the occurances of corresponding keyword i.e. @xref_....@
        // where we will insert Numbers and Hyperlink Fields all pointing to this location
        FindReplaceOptions options = new FindReplaceOptions()
        {
            Direction = FindReplaceDirection.Backward,
            PreserveMetaCharacters = true,
            ReplacingCallback = new FindAndReplace2(lastRun.ParentParagraph.ListLabel.LabelString.Trim(), bmEnd.Name)
        };

        e.MatchNode.Document.Range.Replace(e.Match.Value.Replace("ref_", "xref_"), "", options);

        return ReplaceAction.Skip;
    }
}

private class FindAndReplace2 : IReplacingCallback
{
    public string number;
    public string bookmarkName;

    public FindAndReplace2(string num, string bmName)
    {
        number = num;
        bookmarkName = bmName;
    }

    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        Node currentNode = e.MatchNode;

        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        ArrayList runs = new ArrayList();

        int remainingLength = e.Match.Value.Length;
        while (
            (remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }


        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
        builder.MoveTo((Run)runs[0]);
        builder.Font.StyleIdentifier = StyleIdentifier.Hyperlink;

        FieldHyperlink link = (FieldHyperlink)builder.InsertField(Aspose.Words.Fields.FieldType.FieldHyperlink, false);
        link.SubAddress = bookmarkName;
        link.Result = number;

        link.Update();

        foreach (Run run in runs)
            run.Remove();

        return ReplaceAction.Skip;
    }
}

Hi Awais,

Perfect - with some small modifications solution found - thanks so much

Brian

@caseflow,

Thanks for your feedback. It is great that you have managed to find what you were looking for. Please let us know any time you may have any further queries in future.