We're sorry Aspose doesn't work properply without JavaScript enabled.

Free Support Forum - aspose.com

Aspose.Words - replacement text in wrong place when using IReplacingCallback

Hi,

I am in the process of evaluating Aspose.Words, which I am looking to use to dynamically replace tags/placeholders in Word documents with live data. For example, a tag/placeholder might be [[Firstname]] which at runtime gets replaced with John.

I am just running some very basic initial tests of this, and am seeing strange results with the output text - in certain scenarios, the replacement text is not in the original location of the tag.

Here is my test (in fact, two tests exactly the same just with different input files - see attached):

    // Matches tags of format [[......]]
    public static readonly string AsposeTagRegex = "\\[\\[[^\\[\\]]*\\]\\]";

    [TestMethod]
    public void Test1()
    {
        string dir = "C:\\Users\\Andy\\Documents\\Dev\\Aspose\\";
        string fileName = "test1.docx";
        
        // Load the document from the absolute path on disk.
        Document doc = new Document(dir + fileName);
        
        FindReplaceOptions options = new FindReplaceOptions();
        options.ReplacingCallback = new ReplaceTagsEvaluator(options);
        doc.Range.Replace(new Regex(AsposeTagRegex), String.Empty, options);

        doc.Save(dir + "output1.pdf", SaveFormat.Pdf);
        doc.Save(dir + "output1.docx", SaveFormat.Docx);
    }

and here is the IReplacingCallback implementation:

public class ReplaceTagsEvaluator : IReplacingCallback
{
    internal ReplaceTagsEvaluator(FindReplaceOptions options)
    {
        mOptions = options;
    }

    /// <summary>
    /// NOTE: This is a simplistic method that will only work well when the match
    /// starts at the beginning of a run.
    /// </summary>
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs args)
    {
        DocumentBuilder builder = new DocumentBuilder((Document)args.MatchNode.Document);
        builder.MoveTo(args.MatchNode);

        string match = args.Match.Value;
        Console.WriteLine("Match is: " + match);

        builder.Write("£100");
        args.Replacement = "";

        return ReplaceAction.Replace;
    }

    private readonly FindReplaceOptions mOptions;
}

In the first input file attached (test1.docx), the input text is Rent amount: [[Rent]], and the output is £100Rent amount: ie the replacement text is not where the match is.

In the second input file (test2.docx), the input text is exactly the same, except for an additional space before the [[Rent]], and in this case, the output is as expected, ie: Rent amount: £100

In both tests, the console output for the match is: Match is: [[Rent]].

So the regex is correctly matching the [[Rent]] tag, but for some reason in test1, the replacement text is not in the original location of the tag.

Does anyone have any ideas what I am doing wrong here please?

Many thanks,
Andy

files.zip (134.9 KB)

@aunderwood1,

Please try using the following code:

string AsposeTagRegex = "\\[\\[[^\\[\\]]*\\]\\]";

Document doc = new Document("E:\\Temp\\Files\\test1.docx");

FindReplaceOptions options = new FindReplaceOptions();
options.ReplacingCallback = new ReplaceTagsEvaluator();
doc.Range.Replace(new Regex(AsposeTagRegex), String.Empty, options);

doc.Save("E:\\temp\\Files\\20.4-test1.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;
}

public class ReplaceTagsEvaluator : 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);
        }



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

        string match = e.Match.Value;
        Console.WriteLine("Match is: " + match);

        builder.Write("£100");
        e.Replacement = "";

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

        return ReplaceAction.Skip;
    }
}

Hope, this helps.

That works perfectly - many thanks for your help

@aunderwood1,

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