ReplaceWithHtmlEvaluator only inserts at start of matching node

Hello,

I am curious if it is possible to actually replace an occurrence in a word document in a node or if insert is the only option?

For example, I want to replace an instance of a text tag with some HTML text.

Find: [[REPLACEME]]
Replace: "Some text <br/><br/> some more text"

"Hello! Here I am testing tags by inserting things like [[REPLACEME]] to see if we get text without any extra characters."

With the sample code found here: Find and Replace in C#|Aspose.Words for .NET

I get the following result

"Some text

some more text Hello! Here I am testing tags by inserting things like to see if we get text without any extra characters."

It appears that it can only insert the html fragment at the start of the matched node in the word document. Can you suggest a way to replace the actual text match with the html text instead of prepending?

Thanks

@OpenDevSolutions To achieve what you need you should split the matched Run node. For example see the following code example:

Document doc = new Document(@"C:\Temp\in.docx");
FindReplaceOptions options = new FindReplaceOptions();
options.ReplacingCallback = new ReplaceEvaluatorFindAndReplaceWithHtml();
doc.Range.Replace("[[REPLACEME]]", "Some text <br/><br/> some more text", options);
doc.Save(@"C:\Temp\out.docx");
internal class ReplaceEvaluatorFindAndReplaceWithHtml : IReplacingCallback
{
    /// <summary>
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// </summary>
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        Document doc = (Document)e.MatchNode.Document;

        // 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 deleting.
        List<Run> runs = new List<Run>();

        // 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((Run)currentNode);
            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((Run)currentNode);
        }

        // Create DocumentBuilder to insert HTML.
        DocumentBuilder builder = new DocumentBuilder(doc);
        // Move builder to the first run.
        builder.MoveTo(runs[0]);
        // Insert HTML.
        builder.InsertHtml(e.Replacement);

        // Delete matched runs
        foreach (Run run in runs)
            run.Remove();

        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }

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

Thank you, that did the trick.

1 Like