1
votes

I have a rich text box contentControl in Word template. I'm trying to insert RTF data to the ContentControl and generate a word document out of it. I tried with AltChunk as told here. This will work with SdtBlock. Since the parent of SdtBlock is Body, we can Insert the AltChunk to the body directly. If word document has multiple rich text box contentControls, then word saves the control as SdtRun. The parent to SdtRun is Paragraph and it's parent is Body. If we try to do something like

SdtRun contentControl = (SdtRun)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<SdtRun>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox")); 

contentControl.Parent.Parent.InsertAfter(altChunk, contentControl); //contentControl.Parent.Parent - Returns body of document

throws exception Operation is not valid due to the current state of the object

So I tried to use Paragraph

Paragraph contentControl = (Paragraph)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<Paragraph>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox"));
contentControl.Parent.InsertAfter(altChunk, contentControl); //contentControl.Parent - Returns body of document

This code is inserting data in RTF format. But the problem is I cannot insert to the proper position of Rich TextControl, as we are operating on Paragraph.

Full code is posted below

 string templatePath = @"Template1.docx";
 string newFile = @"Template1_Processed.docx";

 System.IO.File.Copy(templatePath, newFile, true);

 using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(newFile, true))
    {
        Paragraph sdtElement = (Paragraph)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<Paragraph>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox"));
        string innerText = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}{\colortbl ;\red139\green0\blue0;}\viewkind4\uc1\pard\cf1\f0\fs24 test\par}";
        string altChunkId = "myId";
        MainDocumentPart mainDocPart = wordprocessingDocument.MainDocumentPart;
        MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(innerText));

        // Create alternative format import part.
        AlternativeFormatImportPart formatImportPart =
            mainDocPart.AddAlternativeFormatImportPart(
                AlternativeFormatImportPartType.Rtf, altChunkId);

        // Feed HTML data into format import part (chunk).
        formatImportPart.FeedData(ms);
        AltChunk altChunk = new AltChunk();
        altChunk.Id = altChunkId;

        sdtElement.Parent.InsertAfter<AltChunk>(altChunk, sdtElement);
        SdtElement contentControl = (SdtElement)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<SdtElement>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox"));
        contentControl.Remove();
        wordprocessingDocument.MainDocumentPart.CreateRelationshipToPart(formatImportPart);

        sdtElement = (Paragraph)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<Paragraph>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox1"));
        if (sdtElement != null)
        {
            innerText = @"<html><head></head><body><h1>HELLO</h1></body></html>";
            altChunkId = "myId1";
            ms = new MemoryStream(Encoding.UTF8.GetBytes(innerText));

            // Create alternative format import part.
            formatImportPart =
                mainDocPart.AddAlternativeFormatImportPart(
                    AlternativeFormatImportPartType.Html, altChunkId);

            // Feed HTML data into format import part (chunk).
            formatImportPart.FeedData(ms);
            altChunk = new AltChunk();
            altChunk.Id = altChunkId;
            sdtElement.Parent.InsertAfter(altChunk, sdtElement);
            contentControl = (SdtElement)wordprocessingDocument.MainDocumentPart.RootElement.Descendants<SdtElement>().FirstOrDefault(x => x.Descendants<Tag>().Any(y => y.Val == "richtextbox1"));
            contentControl.Remove();

            wordprocessingDocument.MainDocumentPart.CreateRelationshipToPart(formatImportPart);

            wordprocessingDocument.MainDocumentPart.Document.Save();
            wordprocessingDocument.Close();
        }
    }

Attaching the screen shots of Template enter image description hereand generated word document enter image description here

Since we are inserting after to Paragraph, It is getting inserted very next to the Paragraph. So we cannot insert the data to the proper position where rich text data was there in the document.

Can anybody help me to fix this / suggest any alternate method to implement the same?

2
Actually, you can have multiple block level rich text content controls in a docx file (created via Word or otherwise). - JasonPlutext

2 Answers

0
votes

docx4j implements some conventions for content control databinding called "OpenDoPE". (I'm responsible for that name, and more..)

OpenXML allows you to bind a content control to an XML element (in your CustomXML data part) via an XPath expression.

With OpenDoPE, that XML element can contain escaped XHTML, and docx4j will automatically convert that XHTML to real Word content.

There is a version of docx4j for .NET on Nuget

0
votes

What you are trying to do is not a feature of altChunk. altChunk can only insert content at the block level, not at the run level. Further, there is very little that you can do to affect the processing of altChunk content. Word (or Word Automation Services) does what it does, and you can't change that.

Is your only option to import RTF? Do you have any other possible formats, such as XHTML or ?

One really strange idea occurred to me - what if you insert a text box at the location where you want the RTF imported? I am pretty sure that you can import in a text box - you would be, in fact, importing content at the block level. You can create an in-line text box that will move with the text. While not super-easy, you can also impact where the text box is displayed relative to the text. Depending on your actual scenario, this might be possible.

If you were importing HTML, there are possible options to convert the HTML to WordprocessingML at the appropriate point. This is more doable. I don't know of any tools that will convert RTF to WordprocessingML (although it sounds like a fun project!)

-Eric