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();
}
}