Find Bookmarks or Merge Fields in DOCX File and Replace with Icon Images | C# .NET

How to insert an icon in .docx file at a specific position by defining its size and wrap in C#.

@WJobin,

You can insert an icon (.ico) file at any (x, y) position in Word DOCX document by using the following C# code of Aspose.Words for .NET API:

Document doc = new Document("C:\\Temp\\word.docx");
DocumentBuilder builder = new DocumentBuilder(doc);

Shape icon = builder.InsertImage("C:\\Temp\\sample.ico");

icon.RelativeHorizontalPosition = RelativeHorizontalPosition.Page;
icon.RelativeVerticalPosition = RelativeVerticalPosition.Page;

icon.Left = 0 * 72; // 1 inch equals to 72 points
icon.Top = 0 * 72;
//icon.Width = 0.5 * 72;
// icon.Height = 0.5 * 72;

icon.WrapType = WrapType.None;
// icon.WrapSide = WrapSide.Default;

doc.Save("C:\\Temp\\DOCX with icon.docx");
1 Like

@awais.hafeez
In my case the Top will be dynamic, because the paragraphs above the icon can vary.
Actually I want to add a checkbox icon after some paragraphs like below:

<paragraphs created by documentbuilder.writeln()>
File Closed : Yes <Insert checkedIcon>  No <insert uncheckedIcon>

How can I achieve this?

@WJobin,

Please compress the following resources into ZIP format and attach the .zip file here for testing:

  • A simplified source Word document you want to insert checkbox icon in
  • Your expected Word DOCX file showing the desired output. You can create this file manually by using MS Word.

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

1 Like

@awais.hafeez
Please find the attached zip file, which contains the document to test, and another document with expected - final output.
Aspose Word Zip.zip (23.0 KB)

@WJobin,

After performing mail merge, you can use find and replace functionality of Aspose.Words to get the desired results:

Document doc = new Document("C:\\Temp\\Aspose Word Zip\\IconInsert to Test.docx");

FindReplaceOptions opts = new FindReplaceOptions();
opts.Direction = FindReplaceDirection.Backward;
opts.ReplacingCallback = new ReplaceEvaluator();

doc.Range.Replace("{Checked Icon display here}", "C:\\Temp\\Aspose Word Zip\\CheckedIcon.png", opts);
doc.Range.Replace("{Unchecked Icon display here}", "C:\\Temp\\Aspose Word Zip\\UncheckedIcon.png", opts);

doc.Save("C:\\Temp\\Aspose Word Zip\\21.6.docx");

private class ReplaceEvaluator : IReplacingCallback
{
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;

        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();

        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
            (remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }


        //// to insert new content
        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);

        builder.MoveTo((Run)runs[runs.Count - 1]);

        Shape shape = null;
        if (e.Match.Value == "{Checked Icon display here}")
            shape = builder.InsertImage(e.Replacement);

        if (e.Match.Value == "{Unchecked Icon display here}")
            shape = builder.InsertImage(e.Replacement);

        if (shape != null)
            shape.Width = shape.Height = 0.14 * 72;

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

        return ReplaceAction.Skip;
    }

    private 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;
    }
}
1 Like

@awais.hafeez
It works. Thank you for your effort.
I have also found another way, with the help of your first sample code:

  • At first, put a mergeField in the document template where we need to insert the icon.
  • In the code, write :
builder.MoveToMergeField("YesIcon");
Shape yesShape = builder.InsertImage(C:\\Temp\\Aspose Word Zip\\CheckedIcon.png);
yesShape.Width = 10;
yesShape.Height = 10;

builder.MoveToMergeField("NoIcon");
Shape noShape = builder.InsertImage(C:\\Temp\\Aspose Word Zip\\UncheckedIcon.png);
noShape.Width = 10;
noShape.Height = 10;

@WJobin,

Yes, there can be multiple ways to meet this requirement; e.g. instead of merge fields, you can insert Bookmarks (see DocumentBuilder.StartBookmark and DocumentBuilder.EndBookmark methods), then move cursor to Bookmark using DocumentBuilder.MoveToBookmark method and then insert image.

1 Like

@awais.hafeez
Thanks for helping me to figure it out.