private void WriteReplacementHtml(W.SdtElement sdtElement, string html)
        {
            if (String.IsNullOrWhiteSpace(html))
            {
                WriteReplacementText(sdtElement, html);
                return;
            }
            // Replace spaces between images and texts with non breaking spaces
            // http://stackoverflow.com/questions/43659775/regex-to-match-images-followed-with-a-space-in-an-html-string
            html = WordMLTemplateProcessor.ImageFollowedWithSpaceRegex.Replace(html, "$1 ");
            MemoryStream wordMLStream = new MemoryStream();
            try
            {
                ConvertHtmlToWordML(html, wordMLStream);
                WordprocessingDocument convertedDocument = WordprocessingDocument.Open(wordMLStream, false);
                  var convertedElements = convertedDocument.MainDocumentPart.RootElement.Descendants().Single().Elements();
                // Skim the fat (SectionProperties) or risk screwing up your document
                convertedElements = convertedElements.Except(convertedElements.OfType()).ToArray();
                try
                {
                    List numberingIdsProcessed = new List();
                    OpenXmlElement paragraphProperties = null;
                    OpenXmlElement runProperties = null;
                    List insertedElements = new List();
                    // Find the first parent that can contain a block such as a paragraph or a table,
                    // and keep track of the element before which we'll have to insert content
                    for (OpenXmlElement sdtParent = sdtElement.Parent, sdtInsertBefore = sdtElement;
                        sdtParent != null;
                        sdtInsertBefore = sdtParent, sdtParent = sdtParent.Parent)
                    {
                        // Save the paragraph and run properties of the element (if not yet done)
                        if (paragraphProperties == null) paragraphProperties = sdtInsertBefore.Descendants().FirstOrDefault();
                        if (runProperties == null)
                        {
                            W.SdtElement element = sdtInsertBefore as W.SdtElement;
                            if (element != null) runProperties = element.SdtProperties.Descendants().FirstOrDefault();
                        }
                        // Can the parent contain a paragraph or table?
                        if (sdtParent is W.Body
                            || sdtParent is W.Comment
                            || sdtParent is W.CustomXmlBlock
                            || sdtParent is W.DocPartBody
                            || sdtParent is W.Endnote
                            || sdtParent is W.Footnote
                            || sdtParent is W.Footer
                            || sdtParent is W.Header
                            || sdtParent is W.SdtContentBlock
                            || sdtParent is W.TableCell)
                        {
                            // Insert the converted elements as they come
                            foreach (OpenXmlElement convertedElement in convertedElements)
                            {
                                // Insert the element
                                OpenXmlElement insertedElement = convertedElement.CloneNode(true);
                                sdtInsertBefore.InsertBeforeSelf(insertedElement);
                                // Import its numbering from the converted document while avoid conflicts with
                                // numbering IDs in the current document.
                                ResetNumbering(insertedElement, convertedDocument.MainDocumentPart.NumberingDefinitionsPart, numberingIdsProcessed);
                                // Copy the run properties to each run element
                                if (runProperties != null)
                                {
                                    foreach (W.Run run in insertedElement.Descendants())
                                    {
                                        run.InsertAt(runProperties.CloneNode(true), 0);
                                    }
                                }
                                // Copy the paragraph properties to the paragraph
                                if (paragraphProperties != null)
                                {
                                    if (insertedElement is W.Paragraph)
                                    {
                                        // Copy the paragraph properties
                                        insertedElement.InsertAt(paragraphProperties.CloneNode(true), 0);
                                        // If the paragraph properties represent a list item, reset it
                                        // in order to avoid inserting next converted paragraphs as extra list items
                                        W.ParagraphStyleId paragraphStyle = ((W.ParagraphProperties)paragraphProperties).ParagraphStyleId;
                                        if (paragraphStyle != null && paragraphStyle.Val == "ListParagraph")
                                        {
                                            paragraphProperties = null;
                                        }
                                    }
                                    // COMMENTED: 
                                    //  not nice on tables: eg: you're not expecting the table contents to have the
                                    //  same margins as the tag's paragraph
                                    //else
                                    //{
                                    //    foreach (W.Paragraph paragraph in insertedElement.Descendants())
                                    //    {
                                    //        paragraph.InsertAt(paragraphProperties.CloneNode(true), 0);
                                    //    }
                                    //}
                                }
                                // Add to the list of inserted elements
                                insertedElements.Add(insertedElement);
                            }
                            // Remove the replaced element
                            W.SdtBlock sdtBlock = sdtElement as W.SdtBlock;
                            if (sdtBlock != null)
                            {
                                sdtBlock.SdtContentBlock.RemoveAllChildren();
                            }
                            else
                            {
                                // Get the tag associated with the replaced element
                                W.Tag tag = sdtElement.SdtProperties.Elements().FirstOrDefault();
                                // Find the highest parent that contains the replaced tag (and only this one)
                                OpenXmlElement sdtDelete = null;
                                for (OpenXmlElement sdtCandidate = sdtElement.Parent;
                                    sdtCandidate != sdtParent; 
                                    sdtCandidate = sdtCandidate.Parent)
                                {
                                    // Does the candidate contains the replaced tag and only it?
                                    IEnumerable tags = sdtCandidate.Descendants().ToArray();
                                    if (tags.Count() == 1 && tags.First() == tag)
                                    {
                                        // Save it for deletion
                                        sdtDelete = sdtCandidate;
                                        continue;
                                    }
                                    // Stop
                                    break;
                                }
                                // Delete it
                                if (sdtDelete != null)
                                    sdtDelete.Remove();
                            }
                                // Stop
                                break;
                        }
                    }
                    // move images into the target package
                    foreach (OpenXmlElement insertedElement in insertedElements)
                    {
                        ExtractImages(convertedDocument, insertedElement);
                    }
                }
                finally
                {
                    // convertedDocument.Dispose() also disposes the stream it was instantiated with (i.e. wordMLStream)
                    convertedDocument.Dispose();
                    wordMLStream = null;
                }
            }
            finally
            {
                if (wordMLStream != null)
                    wordMLStream.Dispose();
            }
        }