Document.range.replace not working correctly

Hi Tahir,

Thanks for your response.
We can’t give values for rule text and action text hardcoded in IReplacingCallback.Replacing method.
Those values we fetched from JSON based on the configuration for rules and then processed one by one.
And also in document.range.replace method we can replace only one at a time right?
Can you please suggest how to achieve this?

@Deepali_Shirude

You can pass array list of rule text and action text to replace handler to achieve your requirement.

Yes, you can replace the content once at a time. However, in your case we suggest you following solution.

  • Implement IReplacingCallback interface
  • Declare public ArrayList in replace handler class to store matched paragraph nodes
  • Call Range.Replace method as you are already doing.
  • In IReplacingCallback.Replacing, add the matched paragraph nodes to ArrayList. You can get paragraph node of matched node using e.MatchNode.ParentNode property.
  • Once this process is completed, iterate over ArrayList of matched paragraphs and perform find and replace operation for rule and action text.

Hope this helps you.

Hi Tahir,

Thanks for the response.
Can you please give an example for this.

@Deepali_Shirude

Please check the following code example for the solution shared in my previous post. We have attached the input and output documents for your kind reference.
InputDoc.docx (55.6 KB)
21.10.docx (51.5 KB)

Document document = new Document(MyDir + "InputDoc.docx");
document.Range.Replace(ControlChar.NonBreakingSpace, " ", new FindReplaceOptions());
document.JoinRunsWithSameFormatting();

List<string> redlines = new List<string>();
string redline1 = "If level highest standard of care are not followed or the Department determines that the arrangements result in a lack of independence for the DBE involved, no credit for the DBE’s participation as it relates to the material cost will be used toward the Contract goal requirement, and the Contractor will need to make up the difference elsewhere on the project.";
string redline2 = "(vi)                each Non-Lead Securitization Note Holder shall be entitled to the same indemnity as the Lead Securitization Note Holder under the Lead Securitization Servicing Agreement with respect to the following items; each of the Master Servicer, the Special Servicer, the Trustee, the Certificate Administrator, the Operating Advisor, and the Custodian shall be required to indemnify each Certifying Person and each Non-Lead Depositor for any public Other Securitization Trust, and their respective directors and officers and controlling persons, to the same extent that they indemnify the Depositor (as depositor in respect of the Lead Securitization) and each Certifying Person for (a) its failure to deliver the items in clause (vii) below in a timely manner, (b) its failure to perform its obligations to such Non-Lead Depositor or applicable Non-Lead Trustee under Article XI (or any article substantially similar thereto that addresses Exchange act reporting and Regulation AB compliance) of the Lead Securitization Servicing Agreement by the  indemnification time required after giving effect to any applicable grace period or cure period, (c) the failure of any Servicing Function Participant or Additional Servicer retained by it (other than any Initial Sub-Servicer) to perform its obligations to such Non-Lead Depositor or Non-Lead Trustee under Article XI (or any article substantially similar thereto that addresses Exchange act reporting and Regulation AB compliance) of the Lead Securitization Servicing Agreement by the time required after giving effect to any applicable grace period or cure period; and/or (d) any indemnify deficient Exchange act report regarding, and delivered by or on behalf of, such party;";
redlines.Add(redline1);
redlines.Add(redline2);

ReplaceEvaluator replaceEvaluator = new ReplaceEvaluator();

FindReplaceOptions options = new FindReplaceOptions
{
    ReplacingCallback = replaceEvaluator,
    Direction = FindReplaceDirection.Forward,
    FindWholeWordsOnly = true
};


if (redlines != null && redlines.Any())
{
    string actionText = "Professional";
    string ruleText = "highest";

    foreach (var r in redlines)
    {
        if (!string.IsNullOrEmpty(r))
        {
            document.Range.Replace(r, "", options);

            foreach (Run run in replaceEvaluator.nodes.ToArray())
            {
                document.StartTrackRevisions("Admin");
                run.Range.Replace(ruleText, actionText);
                document.StopTrackRevisions();
            }
        }
    }
    document.JoinRunsWithSameFormatting();
    actionText = "negligent act";
    ruleText = "act";

    foreach (Run run in replaceEvaluator.nodes.ToArray())
    {
        document.StartTrackRevisions("Admin");
        run.Range.Replace(ruleText, actionText);
        document.StopTrackRevisions();
    }
}

document.Save(MyDir + "21.10.docx");
public class ReplaceEvaluator : IReplacingCallback
{
    public ArrayList nodes;

    public ReplaceEvaluator()
    {
        nodes = new ArrayList();
    }
    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 highlighting.
        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);
        }

        foreach (Run run in runs)
        {
            nodes.Add(run);
        }

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

    /// <summary>
    /// Splits text of the specified run into two runs.
    /// Inserts the new run just after the specified run.
    /// </summary>
    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        try
        {
            afterRun.Text = run.Text.Substring(position);

            run.Text = run.Text.Substring(0, position);
            run.ParentNode.InsertAfter(afterRun, run);
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }

        return afterRun;
    }
}

Hi Tahir,

Thank you so much for the response.
Will check this.

Hi Tahir,

This solution is working.
Thanks

@Deepali_Shirude

Thanks for your feedback. Please feel free to ask if you have any question about Aspose.Words, we will be happy to help you.