IF Field does not update correctly when left and right expression has RTF content using .NET

Our customers use if-then conditions with mergefields that contain RTF. If quotation marks are contained in the RTF (e.g. “or” or ’or …), the output is incorrect. The if-then condition is not evaluated correctly.

{IF {MERGEFIELD RTF_1} <> “” “{MERGEFIELD RTF_1}” “{MERGEFIELD RTF_2}”

RTF_1 contains, for example: … What makes a speech a “good speech”? …
Everything that is displayed is “good”.

A sample program is attached.

How can you prevent this?

Why does the if-then condition actually persist after the merge in the generated document when I use a FieldMergingCallback? Usually only the result remains, i.e. the formula is completely replaced.

Aspose.Word.Testanwendung.zip (18.4 KB)

@Niebelschutz

Please open the output document in MS Word and update it. MS Word does not show the desired output.

Please manually create your expected Word document that contains the IF fields using Microsoft Word and attach it here for our reference. We will investigate how you want your final Word output be generated like. We will then provide you more information on this along with code.

I hope I understand your answer correctly and can provide you with the information you want.
In the appendix you will find the following files:

  1. Comparison of the output with Aspose - MS Word.png
    I don’t quite understand your answer here (“MS Word does not show the desired output.”)?
    MS Word does not display anything, even after updating. MS Word has evaluated the condition completely and only shows the result.

  2. Output_MsWord_Intermediate_step_after_merge.docx

  3. Codesnippet_transform_RrtfCode_to_Rft.png

  4. Output_MsWord_finalized.docx

Here are a few more explanations:
Since MS Word cannot process RTF text directly during merging, we use the following detour:

  • the RTF code is enclosed in tag’s: [RTF] … [/ RTF]
  • Connect template to data source and execute merge. The result is the file “Output_MsWord_Intermediate_step_after_merge.docx”.
  • Search for RTF code with tag and replace it with RTF (see “Codesnippet_transform_RrtfCode_to_Rft.png”). The result is the file “Output_MsWord_finalized.docx”.

Attachments.zip (88.0 KB)

@Niebelschutz

In your case, we suggest you please do not use mail merge callback. We suggest you following solution to get the desired output.

  1. Insert the RTF as it is into document during mail merge.
  2. Extract the result of IF field.
  3. Import the RTF into Document.
  4. Move the cursor to the IF field.
  5. Insert the RTF document before IF field.
  6. Remove the IF field.

We have modified the code for this scenario. Hope this helps you.

var template = @"Template.docx";
var fileOut = "Output-20.7.docx";

Document doc;
using (var stream = File.Open(template, FileMode.Open, FileAccess.Read, FileShare.Read))
{
	doc = new Document(stream);
}

// please execute once with and once without
var quoteEnsurer = new MergeFieldQuoteEnsurer();
doc.Accept(quoteEnsurer);


var mailMerge = doc.MailMerge;
//mailMerge.FieldMergingCallback = new MergeFieldHandler();
//mailMerge.CleanupOptions = MailMergeCleanupOptions.RemoveUnusedFields | MailMergeCleanupOptions.RemoveContainingFields | MailMergeCleanupOptions.RemoveEmptyParagraphs;
///doc.MailMergeSettings.MainDocumentType = MailMergeMainDocumentType.FormLetters;

DocumentBuilder _builder = new DocumentBuilder(doc);

using (var reader = GetDataIfThenCondition().CreateDataReader())
{
	mailMerge.Execute(reader);

	Field field = doc.Range.Fields[0];
	Console.WriteLine(doc.Range.Fields[0].Result);

	var stream = new MemoryStream(Encoding.UTF8.GetBytes(field.Result));

	var info = FileFormatUtil.DetectFileFormat(stream);
	if (info.LoadFormat == LoadFormat.Rtf)
	{
		var rtfDoc = new Document(stream);

		_builder.MoveToField(field, false);
		_builder.InsertDocument(rtfDoc, ImportFormatMode.KeepSourceFormatting);
		field.Remove();
	}

	doc.Save(fileOut);
}

I tried your suggestion. In principle, it worked too.
Unfortunately, the placeholders (with RTF content) that are not in an if-then condition are not replaced. I.e. the RTF code is displayed. Your solution will not work in this case because the mergefield has been replaced (with the RTF code). As a result, it is no longer in the doc.Range.Fields list and can therefore no longer be replaced.

Is there a solution for both situations?
Can you see in the MergeFieldHandler whether a mergefield is contained in a formula or is “free” in the text?

@Niebelschutz

In this case, please use FieldMergingCallback. Please uncomment following line of code.

mailMerge.FieldMergingCallback = new MergeFieldHandler();

In MergeFieldHandler, please check if the mail merge is nested field of IF field. In true case, please insert the RTF as it is.

In false case, you can load the RTF into document and insert document into template. We have modified the code for your reference. Hope this helps you.

public void FieldMerging(FieldMergingArgs e)
{
	if (_builder == null)
	{
		_builder = new DocumentBuilder(e.Document);
	}

	if (e.FieldName == null || string.IsNullOrWhiteSpace(e.FieldName) ||
		e.FieldValue == null || string.IsNullOrWhiteSpace(e.FieldValue.ToString()))
	{
		return;
	}
	Node node = e.Field.Start;
	node = node.PreviousSibling;
	while (node != null)
	{
		if (node.NodeType == NodeType.FieldStart
			&& ((FieldStart)node).GetField().Type == FieldType.FieldIf)
		{
			return;
		}
		node = node.PreviousSibling;
	}

	if (TryHandleRtfField(e.FieldValue, out var rtfCode))
	{
		var stream = new MemoryStream(Encoding.UTF8.GetBytes(rtfCode));

		var info = FileFormatUtil.DetectFileFormat(stream);
		if (info.LoadFormat == LoadFormat.Rtf)
		{
			var rtfDoc = new Document(stream);

			_builder.MoveToMergeField(e.FieldName);
			_builder.InsertDocument(rtfDoc, ImportFormatMode.KeepSourceFormatting);
			//e.Text = "";
		}
	}
}

I tried it out. Unfortunately, it doesn’t quite work yet.
Checking whether the field is within an IF condition only works for [LeftExpression], which means that the [TrueText] field is replaced in the MergeFieldHandler and the condition is not evaluated correctly again.

Can you extend the test so that the field is recognized in all parts of an IF condition?
IF [LeftExpression] [ComparisionOperator] [RightExpression] [TrueText] [FalseText]

@Niebelschutz

The code shared in my previous post works correctly and generates correct output. Please execute the code and check the output document.

You can get the IF field’s left and right expression along with true and false text after performing mail merge. Please check the following code snippet. Hope this helps you.

mailMerge.Execute(reader);

Field field = doc.Range.Fields[0];

FieldIf fieldIf = (FieldIf)field;
fieldIf.Update();
Console.WriteLine(fieldIf.LeftExpression);
Console.WriteLine(fieldIf.TrueText);
Console.WriteLine(fieldIf.FalseText);
Console.WriteLine(fieldIf.RightExpression);

I don’t think you understood me. It is not about the replacement after the mail merge, but the replacement in the MergeFieldHandler. I shouldn’t do this when it comes to an IF condition.

This position only works for [LeftExpression]. In my example, however, the [TrueText] is a field with RTF code. This is inserted in the MergeFieldHandler with _builder.InsertDocument, which should not be. I.e. he should also make a return.

@Niebelschutz

Please use MailMerge.UnconditionalMergeFieldsAndRegions property as shown below to merge all mail merge fields in the IF field.

mailMerge.UnconditionalMergeFieldsAndRegions = true;
mailMerge.Execute(reader);

Field field = doc.Range.Fields[0];

FieldIf fieldIf = (FieldIf)field;
fieldIf.Update();
Console.WriteLine(fieldIf.LeftExpression);
Console.WriteLine(fieldIf.TrueText);
Console.WriteLine(fieldIf.FalseText);
Console.WriteLine(fieldIf.RightExpression);

The code example shared in my previous post merges the RTF as it e.g. {\rtf1…}. We provided this code because IF field does not work if you insert the RTF using InsertDocument.

Moreover, only those fields are merged as RTF content that are inside IF field. The other fields are merged by InsertDocument method in MergeFieldHandler.

If you still face problem, please ZIP and attach your problematic and expected output documents. Please do not remove IF field from the output documents. We will investigate this issue further and guide you according to your requirement.

Since I have always tested your suggestions in our application and it did not work, I tried it again in the test program that I made available to you. It worked here.
I have now found the difference. In the test program, I filled the DataReader manually. In our application, this is filled from the database. The difference is that the RTF contains line breaks from the database and not in the test program.
These line breaks are the problem with the evaluation:

Do you have a solution for this?

@Niebelschutz

In case you are using old version of Aspose.Wrods, we suggest you please use the latest version of Aspose.Words for .NET 20.7. The code example shared in this thread works correctly using latest version of Aspose.Words with the shared Word document.

This code snippet works for the shared document. However, if you are testing it with different input document and RTF content, we need following resources here for testing:

  • Your input Word document.
  • Please attach the output Word file that shows the undesired behavior.
  • Please attach the expected output Word file that shows the desired behavior.
  • Please do not remove the IF field from the documents.
  • Please create a standalone console application ( source code without compilation errors ) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.

Here the required information: 20200724_Testanwendung.zip (53.8 KB)

@Niebelschutz

We have modified the code example and attached it with this post for your kind reference.

We have also attached the output DOCX with this post. Output 20.7.zip (14.8 KB)

public class MyData
{
    public Bookmark bookmark { get; set; }
    public String rtf { get; set; }
}
public class MergeFieldHandler : IFieldMergingCallback
{
    private const string PatternRtfCode = @"^\{\\rtf.+";
    private int i = 1;
    private MyData data = null;
    public ArrayList rtflist = new ArrayList();

    public void FieldMerging(FieldMergingArgs e)
    {

        if (_builder == null)
        {
            _builder = new DocumentBuilder(e.Document);
        }

        if (e.FieldName == null || string.IsNullOrWhiteSpace(e.FieldName) ||
            e.FieldValue == null || string.IsNullOrWhiteSpace(e.FieldValue.ToString()))
        {
            return;
        }

        Node node = e.Field.Start;
        node = node.PreviousSibling;
        while (node != null)
        {
            if (node.NodeType == NodeType.FieldStart
                && ((FieldStart)node).GetField().Type == FieldType.FieldIf)
            {
                _builder.MoveTo(e.Field.Start);
                BookmarkStart bookmarkStart = _builder.StartBookmark("bookmark" + i);
                _builder.EndBookmark("bookmark" + i);

                data = new MyData();
                data.rtf = e.FieldValue.ToString();
                data.bookmark = bookmarkStart.Bookmark;

                rtflist.Add(data);
                i++;
                e.Text = "";
                return;
            }
            node = node.PreviousSibling;
        }


        if (TryHandleRtfField(e.FieldValue, out var rtfCode))
        {
            var stream = new MemoryStream(Encoding.UTF8.GetBytes(rtfCode));

            var info = FileFormatUtil.DetectFileFormat(stream);
            if (info.LoadFormat == LoadFormat.Rtf)
            {
                var rtfDoc = new Document(stream);

                _builder.MoveToMergeField(e.FieldName);
                _builder.InsertDocument(rtfDoc, ImportFormatMode.KeepSourceFormatting);
                //e.Text = "";
            }
        }
    }

    public void ImageFieldMerging(ImageFieldMergingArgs args)
    {
        // Do nothing.
    }

    private DocumentBuilder _builder;

    private bool TryHandleRtfField(object fieldValue, out string rtfCode)
    {
        if (!(fieldValue is string))
        {
            rtfCode = null;
            return false;
        }

        rtfCode = (string)fieldValue;

        var regex = new Regex(PatternRtfCode, RegexOptions.IgnoreCase);
        if (regex.IsMatch(rtfCode))
        {
            return true;
        }

        rtfCode = null;
        return false;
    }
}

var mailMerge = doc.MailMerge;
MergeFieldHandler handler = new MergeFieldHandler();
mailMerge.FieldMergingCallback = handler;

using (var reader = GetDataIfThenCondition().CreateDataReader())
{
	mailMerge.UnconditionalMergeFieldsAndRegions = true;
	mailMerge.Execute(reader);
	DocumentBuilder builder = new DocumentBuilder(doc);

	foreach (MyData item in handler.rtflist)
	{
		builder.MoveToBookmark(item.bookmark.Name);
		builder.InsertHtml(item.rtf);
	}
	doc.UpdateFields();
	foreach (var field in doc.Range.Fields)
	{
		var stream = new MemoryStream(Encoding.UTF8.GetBytes(field.Result));

		var info = FileFormatUtil.DetectFileFormat(stream);
		if (info.LoadFormat == LoadFormat.Rtf)
		{
			var rtfDoc = new Document(stream);
			builder.MoveToField(field, false);
			builder.InsertDocument(rtfDoc, ImportFormatMode.KeepSourceFormatting);
			field.Remove();
		}
	}

	doc.Save(fileOut);
}

You can download complete project from here:
modified code.zip (53.2 KB)

Please note that Aspose.Words mimics the behavior of MS Word. If you replace the mail merge fields with RTF content using MS Word and update the IF field, the output will not correct. So, you need to workaround this issue by using the attached code example.

Hope this helps you.

Thank you for your response. Unfortunately, the result is still unsatisfactory.
At the first point of the RTF, spaces are inserted if it is in an IF condition.

In addition, the “solution” is extremely adapted to the simple example. I extended the template and added a few “normal” mergefields with formatting. These are not evaluated at all or not correctly. There is another ticket from me (WORDSNET-20396, WORDSNET-20745) that is still open.

I don’t understand why Aspose Words can’t handle normal mail merge functions (apart from the RTF problem)? I would never have expected so many problems when switching from MS Word to Aspose Words.

Maybe I have already received too many workarounds from you (also due to my other inquiries), which hinder each other?

Can you please take another look at the overall situation and provide me with a new solution?

Aspose.Word.Testanwendung.20200727.zip (23.8 KB)

Output_comparison_20200727.zip (32.2 KB)

@Niebelschutz

In your case, you are inserting whole RTF document into IF left expression, Ture and False cases. MS Word also does not handle big RTF content in IF field correctly.

To workaround this issue, you need to use the same approach shared in my previous post. Aspose.Words inserts the bookmark in IF field, insert the content at specific location in the document and update the field correctly.

However, you need to write the code according to your requirement and RTF content that works for IF field in Word document.

Pease let us know if Aspose.Words API works incorrectly in any of your use case. We will investigate the issue and provide you more information on it.

I now have your solution from July 22nd. used. My problem here was the line breaks in the RTF, which is why I had asked for more. But I’m removing it now so that checking if the mergefield is within an IF condition works.
So far everything works now, except for the following problem:
Our customer uses the SuppressBlankLines setting (blank lines resulting from blank fields should not be printed). Unfortunately, this does not work because I cannot use the MailMergeCleanupOptions.RemoveUnusedFields setting, otherwise the RTF replacement will not work.

How can I remove empty lines after the merge (at the point where the RTF is also replaced), i.e. lines that are empty if the result of the IF condition is empty (eg the lines with the conditions with the merge fields NA1, NA2)?

I have adapted the test program: Aspose.Word.Testanwendung.zip (26.9 KB)

@Niebelschutz

In your case, we suggest you following solution.

  1. Please do not remove the IF field using field.Remove(); in your code.
  2. After performing mail merge, please iterate over fields of document.
  3. Check if the field type is FieldIf.
  4. If the field result is empty string, get the parent paragraph of Field using FieldStart.ParentParagraph.
  5. Remove the paragraph using Node.Remove method.

Hope this helps you.

How do I know if the line is empty? Just because the result from the IF is empty, the entire line does not have to be empty, e.g. if the IF condition is in the middle of the text

@Niebelschutz

Please get the parent paragraph of FieldStart (IF field) and convert it to text using Node.ToString method. If the text of paragraph is empty, please remove it.