Writing data to TextInput elements in docx documents using OpenXML 2.5

I have several documents docx. I read them with the OpenXML 2.5 SDK and I am looking TextInputin every document.

        byte[] filebytes = System.IO.File.ReadAllBytes("Test.docx");

        using (MemoryStream stream = new MemoryStream(filebytes))
        using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true))
        {

            IEnumerable<FormFieldData> fields = wordDocument.MainDocumentPart.Document.Descendants<FormFieldData>();
            foreach (var field in fields) 
            {

                IEnumerable<TextInput> textInputs =  field.Descendants<TextInput>();
                foreach (var ti in textInputs)
                {
                    <<HERE>>
                }
            }

            wordDocument.MainDocumentPart.Document.Save();

            stream.Flush(); 
            ETC...
       }

How can I write a value in each TextInput?

Thank!

0
source share
1 answer

First, consider any of the software products (some of which are quite expensive, but can still cost) on the market that provide simple methods for setting the value of formfields.

OpenXML SDK, ( , ), ( , , , - OpenXML SDK , ):

TextInput:

, "" . , textinput.

, "end" . , - , .

, "" . , "end" fieldchar, , "" .

( rPr).

, "end" .
( , , "", , "" ).

.

, "", , "" .
, pPr.

, , "" , "".

:

:

(< br/" > ).
, .

:

, .
, .

- , , "" bookmarkend, , .

, :

private static void SetFormFieldValue(TextInput textInput, string value)
{  // Code for http://stackoverflow.com/a/40081925/3103123

   if (value == null) // Reset formfield using default if set.
   {
      if (textInput.DefaultTextBoxFormFieldString != null && textInput.DefaultTextBoxFormFieldString.Val.HasValue)
         value = textInput.DefaultTextBoxFormFieldString.Val.Value;
   }

   // Enforce max length.
   short maxLength = 0; // Unlimited
   if (textInput.MaxLength != null && textInput.MaxLength.Val.HasValue)
      maxLength = textInput.MaxLength.Val.Value;
   if (value != null && maxLength > 0 && value.Length > maxLength)
      value = value.Substring(0, maxLength);

   // Not enforcing TextBoxFormFieldType (read documentation...).
   // Just note that the Word instance may modify the value of a formfield when user leave it based on TextBoxFormFieldType and Format.
   // A curious example:
   // Type Number, format "# ##0,00".
   // Set value to "2016 was the warmest year ever, at least since 1999.".
   // Open the document and select the field then tab out of it.
   // Value now is "2 016 tht,tt" (the logic behind this escapes me).

   // Format value. (Only able to handle formfields with textboxformfieldtype regular.)
   if (textInput.TextBoxFormFieldType != null
   && textInput.TextBoxFormFieldType.Val.HasValue
   && textInput.TextBoxFormFieldType.Val.Value != TextBoxFormFieldValues.Regular)
      throw new ApplicationException("SetFormField: Unsupported textboxformfieldtype, only regular is handled.\r\n" + textInput.Parent.OuterXml);
   if (!string.IsNullOrWhiteSpace(value)
   && textInput.Format != null
   && textInput.Format.Val.HasValue)
   {
      switch (textInput.Format.Val.Value)
      {
         case "Uppercase":
            value = value.ToUpperInvariant();
            break;
         case "Lowercase":
            value = value.ToLowerInvariant();
            break;
         case "First capital":
            value = value[0].ToString().ToUpperInvariant() + value.Substring(1);
            break;
         case "Title case":
            value = System.Globalization.CultureInfo.InvariantCulture.TextInfo.ToTitleCase(value);
            break;
         default: // ignoring any other values (not supposed to be any)
            break;
      }
   }

   // Find run containing "separate" fieldchar.
   Run rTextInput = textInput.Ancestors<Run>().FirstOrDefault();
   if (rTextInput == null) throw new ApplicationException("SetFormField: Did not find run containing textinput.\r\n" + textInput.Parent.OuterXml);
   Run rSeparate = rTextInput.ElementsAfter().FirstOrDefault(ru =>
      ru.GetType() == typeof(Run)
      && ru.Elements<FieldChar>().FirstOrDefault(fc =>
         fc.FieldCharType == FieldCharValues.Separate)
         != null) as Run;
   if (rSeparate == null) throw new ApplicationException("SetFormField: Did not find run containing separate.\r\n" + textInput.Parent.OuterXml);

   // Find run containg "end" fieldchar.
   Run rEnd = rTextInput.ElementsAfter().FirstOrDefault(ru =>
      ru.GetType() == typeof(Run)
      && ru.Elements<FieldChar>().FirstOrDefault(fc =>
         fc.FieldCharType == FieldCharValues.End)
         != null) as Run;
   if (rEnd == null) // Formfield value contains paragraph(s)
   {
      Paragraph p = rSeparate.Parent as Paragraph;
      Paragraph pEnd = p.ElementsAfter().FirstOrDefault(pa =>
      pa.GetType() == typeof(Paragraph)
      && pa.Elements<Run>().FirstOrDefault(ru =>
         ru.Elements<FieldChar>().FirstOrDefault(fc =>
            fc.FieldCharType == FieldCharValues.End)
            != null)
         != null) as Paragraph;
      if (pEnd == null) throw new ApplicationException("SetFormField: Did not find paragraph containing end.\r\n" + textInput.Parent.OuterXml);
      rEnd = pEnd.Elements<Run>().FirstOrDefault(ru =>
         ru.Elements<FieldChar>().FirstOrDefault(fc =>
            fc.FieldCharType == FieldCharValues.End)
            != null);
   }

   // Remove any existing value.

   Run rFirst = rSeparate.NextSibling<Run>();
   if (rFirst == null || rFirst == rEnd)
   {
      RunProperties rPr = rTextInput.GetFirstChild<RunProperties>();
      if (rPr != null) rPr = rPr.CloneNode(true) as RunProperties;
      rFirst = rSeparate.InsertAfterSelf<Run>(new Run(new[] { rPr }));
   }
   rFirst.RemoveAllChildren<Text>();

   Run r = rFirst.NextSibling<Run>();
   while(r != rEnd)
   {
      if (r != null)
      {
         r.Remove();
         r = rFirst.NextSibling<Run>();
      }
      else // next paragraph
      {
         Paragraph p = rFirst.Parent.NextSibling<Paragraph>();
         if (p == null) throw new ApplicationException("SetFormField: Did not find next paragraph prior to or containing end.\r\n" + textInput.Parent.OuterXml);
         r = p.GetFirstChild<Run>();
         if (r == null)
         {
            // No runs left in paragraph, move other content to end of paragraph containing "separate" fieldchar.
            p.Remove();
            while (p.FirstChild != null)
            {
               OpenXmlElement oxe = p.FirstChild;
               oxe.Remove();
               if (oxe.GetType() == typeof(ParagraphProperties)) continue;
               rSeparate.Parent.AppendChild(oxe);
            }
         }
      }
   }
   if (rEnd.Parent != rSeparate.Parent)
   {
      // Merge paragraph containing "end" fieldchar with paragraph containing "separate" fieldchar.
      Paragraph p = rEnd.Parent as Paragraph;
      p.Remove();
      while (p.FirstChild != null)
      {
         OpenXmlElement oxe = p.FirstChild;
         oxe.Remove();
         if (oxe.GetType() == typeof(ParagraphProperties)) continue;
         rSeparate.Parent.AppendChild(oxe);
      }
   }

   // Set new value.

   if (value != null)
   {
      // Word API use \v internally for newline and \r for para. We treat \v, \r\n, and \n as newline (Break).
      string[] lines = value.Replace("\r\n", "\n").Split(new char[]{'\v', '\n', '\r'});
      string line = lines[0];
      Text text = rFirst.AppendChild<Text>(new Text(line));
      if (line.StartsWith(" ") || line.EndsWith(" ")) text.SetAttribute(new OpenXmlAttribute("xml:space", null, "preserve"));
      for (int i = 1; i < lines.Length; i++)
      {
         rFirst.AppendChild<Break>(new Break());
         line = lines[i];
         text = rFirst.AppendChild<Text>(new Text(lines[i]));
         if (line.StartsWith(" ") || line.EndsWith(" ")) text.SetAttribute(new OpenXmlAttribute("xml:space", null, "preserve"));
      }
   }
   else
   { // An empty formfield of type textinput got char 8194 times 5 or maxlength if maxlength is in the range 1 to 4.
      short length = maxLength;
      if (length == 0 || length > 5) length = 5;
      rFirst.AppendChild(new Text(((char)8194).ToString()));
      r = rFirst;
      for (int i = 1; i < length; i++) r = r.InsertAfterSelf<Run>(r.CloneNode(true) as Run);
   }
}

1. textinput form. xml- , , - . - , Word . , / , OpenXML.

2. Word.
-
-
-
-
-
- ( f.ex 20, ) | Visual Studio document.xml( " ", xml).
, , .

3. formfield.

+3
source

All Articles