Issue with Replace Fields with Static Text

Hi,

I’m using the Aspose Word Sample projects to test some functions in replacing fields and have come across an issue. Using the attached document with just a FieldTextForm and the following code:

Document doc = new Document(dataDir + "Test In.docx");
FieldsHelper.ConvertFieldsToStaticText(doc, FieldType.FieldFormTextInput);
doc.Save(dataDir + "Test Out.docx");

Causes the following error: “Stack Empty”

Further details:

at System.Collections.Stack.Pop()
at x13cd31bb39e0b7ea.xd8102f3a59d221a1.x9eedad928f98421f(FormField x0ab8fc6e4b8e829c)
at x13cd31bb39e0b7ea.xcc0b5baa75272714.VisitFormField(FormField formField)
at Aspose.Words.Fields.FormField.Accept(DocumentVisitor visitor)
at Aspose.Words.CompositeNode.x464d2134480a7bf2(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.CompositeNode.xf7ae36cd24e0b11c(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.Paragraph.Accept(DocumentVisitor visitor)
at Aspose.Words.CompositeNode.x464d2134480a7bf2(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.CompositeNode.xf7ae36cd24e0b11c(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.Body.Accept(DocumentVisitor visitor)
at Aspose.Words.CompositeNode.x464d2134480a7bf2(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.CompositeNode.xf7ae36cd24e0b11c(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.Section.Accept(DocumentVisitor visitor)
at Aspose.Words.CompositeNode.x464d2134480a7bf2(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.CompositeNode.xf7ae36cd24e0b11c(DocumentVisitor x672ff13faf031f3d)
at Aspose.Words.Document.Accept(DocumentVisitor visitor)
at x13cd31bb39e0b7ea.xcc0b5baa75272714.x18dfca7c5fd2402f(x8556eed81191af11 x5ac1382edb7bf2c2)
at Aspose.Words.Document.xf381a641001e6830(Stream xcf18e5243f8d5fd3, String xafe2f3653ee64ebc, SaveOptions xc27f01f21f67608c)
at Aspose.Words.Document.Save(String fileName, SaveOptions saveOptions)
at Aspose.Words.Document.Save(String fileName)
at ReplaceFieldsWithStaticText.Program.ConvertFieldsInDocument(String dataDir) in C:\Program Files (x86)\Aspose\Aspose.Words for .NET\Samples\ReplaceFieldsWithStaticText\CSharp\Program.cs:line 169
at ReplaceFieldsWithStaticText.Program.Main(String[] args) in C:\Program Files (x86)\Aspose\Aspose.Words for .NET\Samples\ReplaceFieldsWithStaticText\CSharp\Program.cs:line 29
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

Any ideas on how to fix this? I have similar issues with FieldType.FieldFormDropDown on another document. Running 13.3.0.0 at the moment but I don’t think it’s a version issue.

Hi Gary,

Thanks for your inquiry. Please add the following method in FieldsHelper class. This will solve the exception issue. I have attached the complete FieldsHelper class with this post. Hope this helps you. Please let us know if you have any more queries.

public override VisitorAction VisitFormField(FormField formfield)
{
    formfield.Remove();
    return VisitorAction.Continue;
}

Thanks Tahir, that fixed the issue with the Text field, but not with FieldFormDropDown. E.g. calling this on the attached document (different to previous one):
FieldsHelper.ConvertFieldsToStaticText(doc, FieldType.FieldFormDropDown)

gives an error in
public override VisitorAction VisitParagraphEnd(Paragraph paragraph)

error: "Object reference not set to an instance of an object"

line: ((Paragraph)nextParagraph).PrependChild(paragraph.LastChild);

Hi Gary,

Thanks for your inquiry. In your case, please use the following code snippet achieve your requirements. This code example removes the form filed from the document and insert the FormField’s result at the place of FormField. Hope this helps you. Please let us know if you have any more queries.

Document doc = new Document(MyDir + "Test+In.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
foreach (FormField field in doc.Range.FormFields)
{
    if (string.IsNullOrEmpty(field.Name))
    {
        String fieldText = field.Result;
        Node currentNode = field;
        while (currentNode != null && currentNode.NodeType != NodeType.FieldStart)
        {
            currentNode = currentNode.PreviousSibling;
        }
        //Insert the Run node with FormField reult before the FieldStart node
        Paragraph para = (Paragraph)currentNode.GetAncestor(NodeType.Paragraph);
        Run run = new Run(doc, fieldText);
        para.InsertBefore(run, currentNode);
        field.Remove();
    }
}
doc.Save(MyDir + "Out.docx");

Thanks Tahir,

That’s a little better, but it’s not quite there. The value is inserted, but the field doesn’t get removed properly (see attached and pres Alt+F9 in Word). It does remove something though as the field can’t be opened as Word complains about it not being added through Word. Any thoughts?

Also, you have adocument builder in the sample code you suggested but it not used in the loop.

Regards,

Hi Tahir,

I’ve made some changes and I think this may fix the issue. Can you see if there’s a problem in doing it this way?

  1. Change the ConvertFieldsToStaticText method to catch DropDown requests and use a different logic:
public static void ConvertFieldsToStaticText(CompositeNode compositeNode, FieldType targetFieldType)
{
    if (targetFieldType == FieldType.FieldFormDropDown)
    {
        ConvertDropDownToStaticText(compositeNode);
    }
    else
    {
        string originalNodeText = compositeNode.ToString(SaveFormat.Text); //ExSkip
        FieldsHelper helper = new FieldsHelper(targetFieldType);
        compositeNode.Accept(helper);

        Debug.Assert(originalNodeText.Equals(compositeNode.ToString(SaveFormat.Text)), "Error: Text of the node converted differs from the original"); //ExSkip
        foreach (Node node in compositeNode.GetChildNodes(NodeType.Any, true)) //ExSkip
            Debug.Assert(!(node is FieldChar && ((FieldChar)node).FieldType.Equals(targetFieldType)), "Error: A field node that should be removed still remains."); //ExSkip 
    }
}
  1. Modify the code you suggested to remove the start/end tags:
public static void ConvertDropDownToStaticText(CompositeNode compositeNode)
{
    for (Int32 i = compositeNode.Range.FormFields.Count - 1; i = 0; i–)
    {
        FormField field = compositeNode.Range.FormFields[i];

        if (field.Type == FieldType.FieldFormDropDown)
        {
            String fieldText = field.Result;
            Node currentNode = field;
            while (currentNode != null && currentNode.NodeType != NodeType.FieldStart)
            {
                currentNode = currentNode.PreviousSibling;
            }

            //Insert the Run node with FormField reult before the FieldStart node
            Paragraph para = (Paragraph)currentNode.GetAncestor(NodeType.Paragraph);
            Run run = new Run(compositeNode.Document, fieldText);
            para.InsertBefore(run, currentNode);
            while (currentNode != null && currentNode.NodeType != NodeType.FieldEnd)
            {
                Node currentNode2 = currentNode;
                currentNode = currentNode.NextSibling;
                currentNode2.Remove();
            }

            currentNode.Remove();
        }
    }
}

Hi Gary,

Thanks for your inquiry. In my previous post, I used the FormField.Remove method which removes itself from the parent. It does not not remove the complete form field. You can use the FormFieldCollection.Remove method to remove the specified form field from the document. Please accept my apologies for your inconvenience.

In your case, I suggest you please use the following code snippet to insert the form field result before the FormField and use FormFieldCollection.Clear method to removes all form fields from this collection and from the document. Hope this helps you.

Document doc = new Document(MyDir + "in.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
foreach (FormField field in doc.Range.FormFields)
{
    String fieldText = field.Result;
    Node currentNode = field;
    while (currentNode != null && currentNode.NodeType != NodeType.FieldStart)
    {
        currentNode = currentNode.PreviousSibling;
    }
    //Insert the Run node with FormField reult before the FieldStart node
    Paragraph para = (Paragraph)currentNode.GetAncestor(NodeType.Paragraph);
    Run run = new Run(doc, fieldText);
    para.InsertBefore(run, currentNode);
}
doc.Range.FormFields.Clear();
doc.Save(MyDir + "Out.docx");

You can also use following code snippet to remove only drop down fields.

// Your code… …
foreach (FieldStart field in doc.GetChildNodes(NodeType.FieldStart, true))
{
    if (field.FieldType == FieldType.FieldFormDropDown)
    {
        field.GetField().Remove();
    }
}
// doc.Range.FormFields.Clear();
doc.Save(MyDir + "Out.docx");

Thanks Tahir, I’ll have a look at it by the end of the week (I didn’t receive a notification of the reply for some reason).

Just a couple of questions,

  1. the first snippet of code removes all FormFields replaces it with their current values? What happens to fields like checkboxes?

  2. Does the 2nd snippet get the current value? It doesn’t look like it should.

Also, is there a way to work this into the FieldsHelper (ie replace the dropdown with it’s value)? I can add exceptions but I think it would be nice if the library can handle this in the FieldsHelper.

Hi Gary,

Thanks for your inquiry.

GaryO:

1) the first snippet of code removes all FormFields replaces it with their current values? What happens to fields like checkboxes?

For a checkbox form field the result can be “1” or “0” to indicate checked or unchecked. The check-boxes are replaced with value 0 or 1.

GaryO:

Also, is there a way to work this into the FieldsHelper (ie replace the dropdown with it’s value)? I can add exceptions but I think it would be nice if the library can handle this in the FieldsHelper.

I have added following ConvertFormFieldToText method in FieldsHelper class to replace form field with its Result. Please find the complete code in attachment. Hope this helps you. Please let us know if you have any more queries.

public static void ConvertFormFieldToText(FormField field)
{
    String fieldText = field.Result;
    Node currentNode = field;
    while (currentNode != null && currentNode.NodeType != NodeType.FieldStart)
    {
        currentNode = currentNode.PreviousSibling;
    }
    //Insert the Run node with FormField reult before the FieldStart node
    Paragraph para = (Paragraph)currentNode.GetAncestor(NodeType.Paragraph);
    Run run = new Run(((FieldStart)currentNode).Document, fieldText);
    para.InsertBefore(run, currentNode);
}

public override VisitorAction VisitFieldStart(FieldStart fieldStart)
{
    if (fieldStart.FieldType == FieldType.FieldFormDropDown || fieldStart.FieldType == FieldType.FieldFormTextInput
    || fieldStart.FieldType == FieldType.FieldFormCheckBox)
    {
        Node currentNode = fieldStart;
        while (currentNode != null && currentNode.NodeType != NodeType.FormField)
        {
            currentNode = currentNode.NextSibling;
        }
        ConvertFormFieldToText((FormField)currentNode);
        fieldStart.GetField().Remove();
    }
    else
    {
        //
        //… … … … … … … …
        //… … … …
    }
}

Thanks Tahir,

Would I still need the following?

tahir.manzoor:

public override VisitorAction VisitFormField(FormField formfield)
{
   formfield.Remove();
   return VisitorAction.Continue;
}

I just did some tests with the FieldsHelper code and it’s asserting this in ConvertFieldsToStaticText(CompositeNode compositeNode, FieldType targetFieldType):

Debug.Assert(originalNodeText.Equals(compositeNode.ToString(SaveFormat.Text)), “Error: Text of the node converted differs from the original”)

Hi Gary,

Thanks for your inquiry. The code shared in my lsast post do not need VisitFormField method. Could you please share the document along with code for following issue. I will investigate the issue on my side and provide you more information.

Debug.Assert(originalNodeText.Equals(compositeNode.ToString(SaveFormat.Text)), “Error: Text of the node converted differs from the original”)

I’ve uploaded a test document and the code used.It seems the changes have affected the non DropDown fields.

Hi Gary,

Thanks for sharing the code and document. I have tested the scenario and have not found any issue with code and output document. Could you please share the detail what issue you are facing while converting form field to static text? It is working correctly at my end.

Hi Tahir,

I just use the code in the project downloaded from samples. The final document isn’t the issue, it’s that it’s triggering the Debug.Assert. I’m not sure why the debug assert is there, but this was not being triggered before.

From the original code in (http://asposewords.codeplex.com/releases/view/51013), it’s line 53:

Debug.Assert(originalNodeText.Equals(compositeNode.ToString(SaveFormat.Text)), "Error: Text of the node converted differs from the original"); //ExSkip

The final document is fine for the simple sample, but I’m afraid to use it in production as it may be indicating a possible problem. Why is the Assert there if it’s not an issue, and why is the suggested modified code triggering the issue?

Hi Gary,

Thanks for your inquiry. In your case, I suggest you please use String.Trim method after convert the Node to string value as shown in following code snippet. This will solve the issue. Please let us know if you have any more queries.

string originalNodeText = compositeNode.ToString(SaveFormat.Text).Trim(); //ExSkip
FieldsHelper2 helper = new FieldsHelper2(targetFieldType);
compositeNode.Accept(helper);
Debug.Assert(originalNodeText.Equals(compositeNode.ToString(SaveFormat.Text).Trim()), "Error: Text of the node converted differs from the original"); //ExSkip
foreach (Node node in compositeNode.GetChildNodes(NodeType.Any, true)) //ExSkip
{
    Debug.Assert(!(node is FieldChar && ((FieldChar)node).FieldType.Equals(targetFieldType)), "Error: A field node that should be removed still remains."); //ExSkip 
    if (node is FieldChar)
        if ((((FieldChar)node).FieldType.Equals(targetFieldType)))
        {
            MessageBox.Show("Error: A field node that should be removed still remains.");
        }
}