From ab3be1ffba670bf207b80993ea07f0014be0fce0 Mon Sep 17 00:00:00 2001 From: Malcolm Johnston <33666206+MalcolmJohnston@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:13:25 +0100 Subject: [PATCH] Added support for HTML fragments in Document Assembler Content tag (#86) * Added inline HTML support in Document Assembler with associated tests and a Tutorial under docs. * Ran csharpier! Updated DA Multiline content test because the HtmlConverter creates paragraphs rather than inserting soft line breaks. * Fixed behaviour of HtmlConverter to allow returning of multiple elements in a Content select (joins them with a new line). Ran csharpier on all files. Added HtmlAgilityPack to paket references. * Fixed issue with duplicate package references as I had added via nuget rather than using paket initially! * fix: no more paket * Removed HTML Agility Pack. Added tests for HTML content (not supported) and non-well formed XHTML. Looking at possibility of replacing XMLReader approach with something closer to HtmlToWmlConverter. * Work in progress. Builds! * Updated to use HtmlToWmlConverter type approach. * Csharpier fixes. --------- Co-authored-by: Sergey Tihon --- Clippit.Tests/Word/DocumentAssemblerTests.cs | 13 +- Clippit/Html/HtmlToWmlConverterCore.cs | 42 +- Clippit/Word/Assembler/HtmlConverter.cs | 395 ++++++++++++++++++ Clippit/Word/Assembler/UriExtensions.cs | 12 + Clippit/Word/Assembler/XPathExtensions.cs | 5 - Clippit/Word/DocumentAssembler.cs | 146 +++---- TestFiles/DA/DA-html-input.xml | 40 ++ TestFiles/DA/DA289A-xhtml-formatting.docx | Bin 0 -> 22879 bytes TestFiles/DA/DA289B-html-not-supported.docx | Bin 0 -> 20480 bytes .../DA/DA289C-not-well-formed-xhtml.docx | Bin 0 -> 19764 bytes .../inline-HTML-in-Word-example.png | Bin 0 -> 1874 bytes .../DocumentAssembler_InlineHtmlSupport.md | 64 +++ docs/tutorials/word/toc.yml | 4 +- 13 files changed, 602 insertions(+), 119 deletions(-) create mode 100644 Clippit/Word/Assembler/HtmlConverter.cs create mode 100644 Clippit/Word/Assembler/UriExtensions.cs create mode 100644 TestFiles/DA/DA-html-input.xml create mode 100644 TestFiles/DA/DA289A-xhtml-formatting.docx create mode 100644 TestFiles/DA/DA289B-html-not-supported.docx create mode 100644 TestFiles/DA/DA289C-not-well-formed-xhtml.docx create mode 100644 docs/images/word/documentassembler/inline-HTML-in-Word-example.png create mode 100644 docs/tutorials/word/DocumentAssembler_InlineHtmlSupport.md diff --git a/Clippit.Tests/Word/DocumentAssemblerTests.cs b/Clippit.Tests/Word/DocumentAssemblerTests.cs index da5c92f7..770075fc 100644 --- a/Clippit.Tests/Word/DocumentAssemblerTests.cs +++ b/Clippit.Tests/Word/DocumentAssemblerTests.cs @@ -154,6 +154,9 @@ public DocumentAssemblerTests(ITestOutputHelper log) [InlineData("DA285-ImageSelectNoParagraphFollowedAfterMetadata.docx", "DA-Data-WithImages.xml", true)] [InlineData("DA285A-ImageSelectNoParagraphFollowedAfterMetadata.docx", "DA-Data-WithImages.xml", true)] [InlineData("DA-I0038-TemplateWithMultipleXPathResults.docx", "DA-I0038-Data.xml", false)] + [InlineData("DA289A-xhtml-formatting.docx", "DA-html-input.xml", false)] + [InlineData("DA289B-html-not-supported.docx", "DA-html-input.xml", true)] + [InlineData("DA289C-not-well-formed-xhtml.docx", "DA-html-input.xml", true)] public void DA101(string name, string data, bool err) { var templateDocx = new FileInfo(Path.Combine(_sourceDir.FullName, name)); @@ -185,14 +188,8 @@ public void DA259(string name, string data, bool err) Path.Combine(TempDir, name.Replace(".docx", "-processed-by-DocumentAssembler.docx")) ); var afterAssembling = new WmlDocument(assembledDocx.FullName); - var brCount = afterAssembling - .MainDocumentPart.Element(W.body) - .Elements(W.p) - .ElementAt(1) - .Elements(W.r) - .Elements(W.br) - .Count(); - Assert.Equal(4, brCount); + var brCount = afterAssembling.MainDocumentPart.Element(W.body).Elements(W.p).Count(); + Assert.Equal(6, brCount); } [Theory] diff --git a/Clippit/Html/HtmlToWmlConverterCore.cs b/Clippit/Html/HtmlToWmlConverterCore.cs index 6c785a27..47d333af 100644 --- a/Clippit/Html/HtmlToWmlConverterCore.cs +++ b/Clippit/Html/HtmlToWmlConverterCore.cs @@ -824,7 +824,7 @@ private static object NormalizeTransform(XNode node) return node; } - private enum NextExpected + internal enum NextExpected { Paragraph, Run, @@ -2830,7 +2830,7 @@ string pictureDescription #endif - private static XElement GetParagraphProperties( + internal static XElement GetParagraphProperties( XElement blockLevelElement, string styleName, HtmlToWmlConverterSettings settings @@ -3041,14 +3041,18 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv return new XElement[] { spacing, ind, contextualSpacing }; } - private static XElement GetRunProperties(XText textNode, HtmlToWmlConverterSettings settings) + internal static XElement GetRunProperties(XText textNode, HtmlToWmlConverterSettings settings) { var parent = textNode.Parent; - var rPr = GetRunProperties(parent, settings); - return rPr; + if (parent != null) + { + return GetRunProperties(parent, settings); + } + + return new XElement(W.rPr); } - private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSettings settings) + internal static XElement GetRunProperties(XElement element, HtmlToWmlConverterSettings settings) { var colorProperty = element.GetProp("color"); var fontFamilyProperty = element.GetProp("font-family"); @@ -3060,15 +3064,15 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet var letterSpacingProperty = element.GetProp("letter-spacing"); var directionProp = element.GetProp("direction"); - var colorPropertyString = colorProperty.ToString(); + var colorPropertyString = colorProperty?.ToString(); var fontFamilyString = GetUsedFontFromFontFamilyProperty(fontFamilyProperty); var fontSizeTPoint = GetUsedSizeFromFontSizeProperty(fontSizeProperty); - var textDecorationString = textDecorationProperty.ToString(); - var fontStyleString = fontStyleProperty.ToString(); - var fontWeightString = fontWeightProperty.ToString().ToLower(); - var backgroundColorString = backgroundColorProperty.ToString().ToLower(); - var letterSpacingString = letterSpacingProperty.ToString().ToLower(); - var directionString = directionProp.ToString().ToLower(); + var textDecorationString = textDecorationProperty?.ToString(); + var fontStyleString = fontStyleProperty?.ToString(); + var fontWeightString = fontWeightProperty?.ToString().ToLower(); + var backgroundColorString = backgroundColorProperty?.ToString().ToLower(); + var letterSpacingString = letterSpacingProperty?.ToString().ToLower(); + var directionString = directionProp?.ToString().ToLower(); var subAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sub).Any(); var supAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sup).Any(); @@ -3085,7 +3089,7 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet dirAttributeString = dirAttribute.Value.ToLower(); XElement shd = null; - if (backgroundColorString != "transparent") + if (backgroundColorString != null && backgroundColorString != "transparent") shd = new XElement( W.shd, new XAttribute(W.val, "clear"), @@ -3155,7 +3159,7 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet rStyle = new XElement(W.rStyle, new XAttribute(W.val, "Hyperlink")); XElement spacing = null; - if (letterSpacingProperty.IsNotNormal) + if (letterSpacingProperty != null && letterSpacingProperty.IsNotNormal) spacing = new XElement(W.spacing, new XAttribute(W.val, (long)(Twip)letterSpacingProperty)); XElement rtl = null; @@ -3191,9 +3195,9 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet // todo this is not right - needs to be rationalized for all characters in an entire paragraph. // if there is text like

abc def ghi

then there needs to be just one space between abc and def, and between // def and ghi. - private static string GetDisplayText(XText node, bool preserveWhiteSpace) + internal static string GetDisplayText(XText node, bool preserveWhiteSpace) { - var textTransform = node.Parent.GetProp("text-transform").ToString(); + var textTransform = node.Parent.GetProp("text-transform")?.ToString(); var isFirst = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.FirstNode; var isLast = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.LastNode; @@ -3884,7 +3888,7 @@ private static XElement GetTableRowProperties(XElement element) return trPr; } - private static XAttribute GetXmlSpaceAttribute(string value) + internal static XAttribute GetXmlSpaceAttribute(string value) { if (value.StartsWith(" ") || value.EndsWith(" ")) return new XAttribute(XNamespace.Xml + "space", "preserve"); @@ -4331,7 +4335,7 @@ private static XElement GetBackgroundProperty(XElement element) var color = element.GetProp("background-color"); // todo this really should test against default background color - if (color.ToString() != "transparent") + if (color != null && color.ToString() != "transparent") { var hexString = color.ToString(); var shd = new XElement( diff --git a/Clippit/Word/Assembler/HtmlConverter.cs b/Clippit/Word/Assembler/HtmlConverter.cs new file mode 100644 index 00000000..4061e6c9 --- /dev/null +++ b/Clippit/Word/Assembler/HtmlConverter.cs @@ -0,0 +1,395 @@ +using System.Collections; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; +using Clippit.Html; +using Clippit.Internal; +using DocumentFormat.OpenXml.Packaging; +using NextExpected = Clippit.Html.HtmlToWmlConverterCore.NextExpected; + +namespace Clippit.Word.Assembler +{ + internal static class HtmlConverter + { + private static readonly HtmlToWmlConverterSettings htmlConverterSettings = + HtmlToWmlConverter.GetDefaultSettings(); + + private static readonly Regex detectEntityRegEx = new Regex("^&(?:#([0-9]+)|#x([0-9a-fA-F]+)|([0-9a-zA-Z]+));"); + + /// + /// Method processes a string that contains inline html tags and generates a run with the necessary properties + /// Supported inline html tags: b, i, em, strong, u, br, a + /// Supported block tags: p, div + /// TODO: add support for the following html tags: big, small, sub, sup, span. + /// + /// Source element. + /// Data element with content. + /// The paragraph properties. + /// Error indicator. + internal static IEnumerable ProcessContentElement( + this XElement element, + XElement data, + TemplateError templateError, + ref OpenXmlPart part + ) + { + var xPath = (string)element.Attribute(PA.Select); + var optionalString = (string)element.Attribute(PA.Optional); + bool optional = (optionalString != null && optionalString.ToLower() == "true"); + + string[] values = data.EvaluateXPath(xPath, optional); + + // if we no data returned then just return an empty run + if (values.Length == 0) + { + return new[] { new XElement(W.r, W.t) }; + } + + // otherwise split the values if there are new line characters + values = values + .SelectMany(x => x.Replace("\r\n", "\n", StringComparison.OrdinalIgnoreCase).Split('\n')) + .ToArray(); + + List results = new List(); + for (int i = 0; i < values.Length; i++) + { + // try processing as XML + XElement parsedElement = XElement.Parse($"{EscapeAmpersands(values[i])}"); + + results.Add( + Transform( + parsedElement, + htmlConverterSettings, + part, + i == 0 ? NextExpected.Run : NextExpected.Paragraph, + true + ) + ); + } + + results = FlattenResults(results); + + if (results.Count == 0) + { + return new[] { new XElement(W.r, W.t) }; + } + + return results; + } + + private static List FlattenResults(IEnumerable content) + { + // flatten the returned content + List results = new List(); + foreach (object obj in content) + { + if (obj is IEnumerable) + { + results.AddRange(FlattenResults(obj as IEnumerable)); + } + else + { + results.Add(obj); + } + } + + return results; + } + + private static object Transform( + XNode node, + HtmlToWmlConverterSettings settings, + OpenXmlPart part, + NextExpected nextExpected, + bool preserveWhiteSpace + ) + { + var element = node as XElement; + if (element != null) + { + if (element.Name == XhtmlNoNamespace.a) + { + var rId = Relationships.GetNewRelationshipId(); + var href = (string)element.Attribute(NoNamespace.href); + if (href != null) + { + Uri uri = null; + try + { + uri = href.GetUri(); + } + catch (UriFormatException) + { + var rPr = HtmlToWmlConverterCore.GetRunProperties(element, settings); + var run = new XElement(W.r, rPr, new XElement(W.t, element.Value)); + return new[] { run }; + } + + if (uri != null) + { + part.AddHyperlinkRelationship(uri, true, rId); + if (element.Element(XhtmlNoNamespace.img) != null) + { + var imageTransformed = element + .Nodes() + .Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)) + .OfType(); + var newImageTransformed = imageTransformed + .Select(i => + { + if (i.Elements(W.drawing).Any()) + { + var newRun = new XElement(i); + var docPr = newRun + .Elements(W.drawing) + .Elements(WP.inline) + .Elements(WP.docPr) + .FirstOrDefault(); + if (docPr != null) + { + var hlinkClick = new XElement( + A.hlinkClick, + new XAttribute(R.id, rId), + new XAttribute(XNamespace.Xmlns + "a", A.a.NamespaceName) + ); + docPr.Add(hlinkClick); + } + return newRun; + } + return i; + }) + .ToList(); + return newImageTransformed; + } + + var rPr = HtmlToWmlConverterCore.GetRunProperties(element, settings); + + var hyperlink = new XElement( + W.hyperlink, + new XAttribute(R.id, rId), + new XElement(W.r, rPr, new XElement(W.t, element.Value)) + ); + + if (nextExpected == NextExpected.Paragraph) + { + return new XElement(W.p, hyperlink); + } + + return new[] { hyperlink }; + } + } + return null; + } + + if (element.Name == XhtmlNoNamespace.b) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.div) + { + if (nextExpected == NextExpected.Paragraph) + { + if ( + element + .Descendants() + .Any(d => d.Name == XhtmlNoNamespace.li || d.Name == XhtmlNoNamespace.p) + ) + { + return element + .Nodes() + .Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + } + else + { + return GenerateNextExpected(element, settings, part, null, nextExpected, false); + } + } + else + { + return element + .Nodes() + .Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + } + } + + if (element.Name == XhtmlNoNamespace.em) + return element + .Nodes() + .Select(n => Transform(n, settings, part, NextExpected.Run, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.html) + return element + .Nodes() + .Select(n => Transform(n, settings, part, NextExpected.Paragraph, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.i) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.li) + { + return GenerateNextExpected(element, settings, part, null, NextExpected.Paragraph, false); + } + + if (element.Name == XhtmlNoNamespace.ol) + return element + .Nodes() + .Select(n => Transform(n, settings, part, NextExpected.Paragraph, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.p) + { + return GenerateNextExpected(element, settings, part, null, NextExpected.Paragraph, false); + } + + if (element.Name == XhtmlNoNamespace.strong) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.sub) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.sup) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.u) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.ul) + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + + if (element.Name == XhtmlNoNamespace.br) + if (nextExpected == NextExpected.Paragraph) + { + return new XElement(W.p, new XElement(W.r, new XElement(W.t))); + } + else + { + return new XElement(W.r); + } + + // if no match up to this point, then just recursively process descendants + return element.Nodes().Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)); + } + + // process text nodes unless their parent is a title tag + if (node.Parent.Name != XhtmlNoNamespace.title) + return GenerateNextExpected(node, settings, part, null, nextExpected, preserveWhiteSpace); + + return null; + } + + private static object GenerateNextExpected( + XNode node, + HtmlToWmlConverterSettings settings, + OpenXmlPart part, + string styleName, + NextExpected nextExpected, + bool preserveWhiteSpace + ) + { + if (nextExpected == NextExpected.Paragraph) + { + var element = node as XElement; + if (element != null) + { + return new XElement( + W.p, + element.Nodes().Select(n => Transform(n, settings, part, NextExpected.Run, preserveWhiteSpace)) + ); + } + else + { + var xTextNode = node as XText; + if (xTextNode != null) + { + var textNodeString = HtmlToWmlConverterCore.GetDisplayText(xTextNode, preserveWhiteSpace); + var p = new XElement( + W.p, + new XElement( + W.r, + HtmlToWmlConverterCore.GetRunProperties(xTextNode, settings), + new XElement( + W.t, + HtmlToWmlConverterCore.GetXmlSpaceAttribute(textNodeString), + textNodeString + ) + ) + ); + return p; + } + return null; + } + } + else + { + var element = node as XElement; + if (element != null) + { + return element + .Nodes() + .Select(n => Transform(n, settings, part, nextExpected, preserveWhiteSpace)) + .AsEnumerable(); + } + else + { + var textNodeString = HtmlToWmlConverterCore.GetDisplayText((XText)node, preserveWhiteSpace); + var rPr = HtmlToWmlConverterCore.GetRunProperties((XText)node, settings); + var r = new XElement( + W.r, + rPr, + new XElement(W.t, HtmlToWmlConverterCore.GetXmlSpaceAttribute(textNodeString), textNodeString) + ); + return r; + } + } + } + + private static string EscapeAmpersands(string value) + { + // check whether we have any processing to do + if (!string.IsNullOrWhiteSpace(value) && value.Contains('&', StringComparison.OrdinalIgnoreCase)) + { + string result = string.Empty; + + int ampIndex = value.IndexOf('&', StringComparison.OrdinalIgnoreCase); + while (ampIndex >= 0) + { + // put everything before the ampersand into the result + result += value.Substring(0, ampIndex); + + // then trim the value back + value = value.Substring(ampIndex); + + // now check whether ampersand we have found is the start of an entity + Match m = detectEntityRegEx.Match(value); + if (m.Success) + { + // if this is an entity then add to result + result += value.Substring(0, m.Length); + + // then remove entity from input + value = value.Substring(m.Length); + } + else + { + // add escaped ampersand to result + result += "&"; + + // then remove ampersand from input + value = value.Substring(1); + } + + ampIndex = value.IndexOf('&', StringComparison.OrdinalIgnoreCase); + } + + // add any remaining string + if (!string.IsNullOrEmpty(value)) + { + result += value; + } + + return result; + } + + return value; + } + } +} diff --git a/Clippit/Word/Assembler/UriExtensions.cs b/Clippit/Word/Assembler/UriExtensions.cs new file mode 100644 index 00000000..ec27feaa --- /dev/null +++ b/Clippit/Word/Assembler/UriExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace Clippit.Word.Assembler +{ + internal static class UriExtensions + { + internal static Uri GetUri(this string s) + { + return new UriBuilder(s).Uri; + } + } +} diff --git a/Clippit/Word/Assembler/XPathExtensions.cs b/Clippit/Word/Assembler/XPathExtensions.cs index ee275082..9234d290 100644 --- a/Clippit/Word/Assembler/XPathExtensions.cs +++ b/Clippit/Word/Assembler/XPathExtensions.cs @@ -1,11 +1,6 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; using System.Xml.Linq; using System.Xml.XPath; diff --git a/Clippit/Word/DocumentAssembler.cs b/Clippit/Word/DocumentAssembler.cs index 9aac9a8a..b4d4c08a 100644 --- a/Clippit/Word/DocumentAssembler.cs +++ b/Clippit/Word/DocumentAssembler.cs @@ -6,13 +6,16 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.Remoting; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using System.Xml.Schema; using System.Xml.XPath; +using Clippit.Internal; using Clippit.Word.Assembler; using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using Path = System.IO.Path; @@ -843,47 +846,6 @@ private static string ValidatePerSchema(XElement element) private static Dictionary s_paSchemaSets; - /// - /// Gets the next image relationship identifier of given part. The - /// parts can be either header, footer or main document part. The method - /// scans for already present relationship identifiers, then increments and - /// returns the next available value. - /// - /// The part. - /// System.String. - private static string GetNextImageRelationshipId(OpenXmlPart part) - { - switch (part) - { - case MainDocumentPart mainDocumentPart: - { - var imageId = mainDocumentPart - .Parts.Select(p => Regex.Match(p.RelationshipId, @"rId(?\d+)").Groups["rId"].Value) - .Max(Convert.ToDecimal); - - return $"rId{++imageId}"; - } - case HeaderPart headerPart: - { - var imageId = headerPart - .Parts.Select(p => Regex.Match(p.RelationshipId, @"rId(?\d+)").Groups["rId"].Value) - .Max(Convert.ToDecimal); - - return $"rId{++imageId}"; - } - case FooterPart footerPart: - { - var imageId = footerPart - .Parts.Select(p => Regex.Match(p.RelationshipId, @"rId(?\d+)").Groups["rId"].Value) - .Max(Convert.ToDecimal); - - return $"rId{++imageId}"; - } - default: - return null; - } - } - /// /// Calculates the maximum docPr id. The identifier is /// unique throughout the document. This method @@ -1003,7 +965,7 @@ OpenXmlPart part // assign unique image and paragraph ids. Image id is document property Id (wp:docPr) // and relationship id is rId. Their numbering is different. const string imageId = InvalidImageId; // Ids will be replaced with real ones later, after transform is done - var relationshipId = GetNextImageRelationshipId(part); + var relationshipId = Relationships.GetNewRelationshipId(); var inline = para.Descendants(W.drawing).Descendants(WP.inline).FirstOrDefault(); if (inline == null) @@ -1303,63 +1265,80 @@ OpenXmlPart part } if (element.Name == PA.Content) { - if (element.Descendants(A.r).FirstOrDefault() is not null) + XElement parentPara = element.Ancestors(W.p).FirstOrDefault(); // is the Content element in a paragraph + XElement embeddedPara = element.Descendants(W.p).FirstOrDefault(); // does the Content element contain a paragraph + + // if so create a new paragraph to add our content to + if (embeddedPara != null) { - return ProcessAParagraph(element, data, templateError); - } + // get the current paragraph properties + XElement pProps = embeddedPara.Descendants(W.pPr).FirstOrDefault(); - var para = element.Descendants(W.p).FirstOrDefault(); - var run = element.Descendants(W.r).FirstOrDefault(); + // create a new paragraph to return + embeddedPara = new XElement(W.p); - var xPath = (string)element.Attribute(PA.Select); - var optionalString = (string)element.Attribute(PA.Optional); - var optional = (optionalString != null && optionalString.ToLower() == "true"); + // add the paragraph properties + if (pProps != null) + { + embeddedPara.Add(pProps); + } + } - string[] newValues; + XElement currentPara = embeddedPara ?? parentPara; + XElement currentParaProps = currentPara.Descendants(W.pPr).FirstOrDefault(); + + // get the list of created elements, could be all paragraphs or a run followed by paragraphs + IList content; try { - newValues = data.EvaluateXPath(xPath, optional); + content = element.ProcessContentElement(data, templateError, ref part).ToList(); } - catch (XPathException e) + catch (Exception ex) { - return element.CreateContextErrorMessage("XPathException: " + e.Message, templateError); + return element.CreateContextErrorMessage($"Content: {ex.Message}", templateError); } - var lines = newValues.SelectMany(x => x.Split('\n')); - if (para is not null) + // get XElements and ensure all but the first element is in a + List elements = new List(); + for (int i = 0; i < content.Count; i++) { - var p = new XElement(W.p, para.Elements(W.pPr)); - var rPr = para.Elements(W.r).Elements(W.rPr).FirstOrDefault(); - foreach (var line in lines) + object obj = content[i]; + if (obj is XElement) { - p.Add( - new XElement( - W.r, - rPr, - (p.Elements().Count() > 1) ? new XElement(W.br) : null, - new XElement(W.t, line) - ) - ); + var objEl = obj as XElement; + if (i > 0 && objEl.Name == W.r || objEl.Name == W.hyperlink) + { + elements.Add(new XElement(W.p, currentParaProps, content[i])); + } + else + { + elements.Add(objEl); + } } - return p; } - else + + // add all but the first element after the current paragraph + for (int i = elements.Count - 1; i > 0; i--) { - var list = new List(); - var rPr = run.Elements().Where(e => e.Name != W.t); - foreach (var line in lines) + if (embeddedPara == null && parentPara != null) { - list.Add( - new XElement( - W.r, - rPr, - (list.Count > 0) ? new XElement(W.br) : null, - new XElement(W.t, line) - ) - ); + parentPara.AddAfterSelf(elements[i]); + } + else + { + element.AddAfterSelf(elements[i]); } - return list; } + + // return first element wrapped in the embedded para if we do not have a paragraph + if (elements[0].Name != W.p && embeddedPara != null) + { + embeddedPara.Add(elements[0]); + return embeddedPara; + } + + // or simply return the first element + return elements[0]; } if (element.Name == PA.Repeat) { @@ -1382,11 +1361,6 @@ OpenXmlPart part if (optional) { return null; - //XElement para = element.Descendants(W.p).FirstOrDefault(); - //if (para != null) - // return new XElement(W.p, new XElement(W.r)); - //else - // return new XElement(W.r); } return element.CreateContextErrorMessage("Repeat: Select returned no data", templateError); } diff --git a/TestFiles/DA/DA-html-input.xml b/TestFiles/DA/DA-html-input.xml new file mode 100644 index 00000000..a64e4f64 --- /dev/null +++ b/TestFiles/DA/DA-html-input.xml @@ -0,0 +1,40 @@ + + + <p><b>Some Bold Text</b></p> + <i>Some Italic Text</i> + <u>Some Underline Text</u> + <a href="https://www.google.co.uk">Google</a> + <i><b>Some Bold Italic Text</b></i> + <u><b>Some Bold Underline Text</b></u> + <i><u>Some Italic Underline Text</u></i> + <b><i><u>Some Bold Italic Underline Text</u></i></b> + <b><a href="https://www.google.co.uk">Bold Google</a></b> + <i><a href="https://www.google.co.uk">Italic Google</a></i> + <u><a href="https://www.google.co.uk">Underline Google</a></u> + <i><b><a href="https://www.google.co.uk">Bold Italic Google</a></b></i> + <u><b><a href="https://www.google.co.uk">Bold Underline Google</a></b></u> + <u><i><a href="https://www.google.co.uk">Italic Underline Google</a></i></u> + <b><u><i><a href="https://www.google.co.uk">Bold Italic Underline Google</a></i></u></b> + <a href="www.google.co.uk">Google</a> + This content comes from CDATA

+ ]]> +
+ What follows is Subscript!

+

What follows is more Subscript! followed by a break
and some more text.

+ ]]> +
+ What follows is Superscript!

+ ]]> +
+ What follows is Struckout!

+ ]]> +
+ + This is NOT valid XHTML.<br>But is Valid HTML. + + <b><i>This is NOT well formed XHTML.</b></i> +
\ No newline at end of file diff --git a/TestFiles/DA/DA289A-xhtml-formatting.docx b/TestFiles/DA/DA289A-xhtml-formatting.docx new file mode 100644 index 0000000000000000000000000000000000000000..3a2fa70d681d90ddac2ae6025d5902fdd69ffe4f GIT binary patch literal 22879 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 literal 0 HcmV?d00001 diff --git a/TestFiles/DA/DA289C-not-well-formed-xhtml.docx b/TestFiles/DA/DA289C-not-well-formed-xhtml.docx new file mode 100644 index 0000000000000000000000000000000000000000..642a9c697e7d81c82c709fbe00bf93845c7f48a7 GIT binary patch literal 19764 zcmeI4WpEw4wxFHZF*7sA%oH;-#mr1GGcz+Y#>~vj%yyg|J7#8P&g`6f?}422-qie> zAMbV5R_#`6tyZh0mh>$x$w`5Np@M*eK!SjP5P=~2@!NU60RcIH00BV(fqbheWMl1U zZ0)G4 z4!If`S%J>owgOj29&`%_<#6oQH|aF!^AI)Etla%MjpX4b^U?bhwv5CAY3C)&^#->p zCe!Q2j4L)!VfQNqa+yNZv5JRgd%Y5t^$p78u9FMIw@}e@H2!AwF2KD~A{M9a!9f0w57;z+`~U@!`%9z6 zkHc<01J*=YV4H*iHd7G-p>Hbe-`i(Fxx4=*o&Dj zOK&){g!vr;Qd2@2Wof~D{`G}>Vd1Uzfxg)Im$`%)cZW<-r}aebbL?bgxbRM>Zx1^C znok|BAW1Y&T&uc5h8y#z-V6#{weM(ZZ*(!Q<{jQzv^+cBq7JcZy-mh$*MP z8j{xMf3{Pezf1LI&Pihgd@~o<^nfVi5$OuXYn#IIhBo1=O<|^ujp zGN-(u!M0?ef1ep=Pyvm7-Z6XVp5I+O7#2hWl?xR{|Kw?+g{<+q7ru+d$96`f-k`f- zgOjN(aP^Pv!{4&}juZ?8L<0c?1PNFuE;jZ?^hP#@PFBF4?Pr%Zk)j#1$%g8?Mt#F4 z?6%OHwk#x`qRv=z9R0OY?o-(Y{bf^VxAE zv}ri9m?it*TW6^SdyHx3ZhPA6@c1F!3@e=+K8c_M_gqZsQ8vE!>wzKK5DOJ^CJY=R z2SPb<`au|?h$$wwq;2rMSF&iNeH+>x(j;u#N)Zwp)tEs)8?E9Ev?$30M`kns1HN5n zuSkepLuxK`;QJZYJTOK-1fKpj>`g315={SN9DZA&CG5P+a``TzMr_~Z4E`K|@AitQ z`;Psq6{&}K`m(5faKSp;7sAXsec!pado?YEG(vBKEk7BBdeKL4fnbOi06%=2kkWHD zj1-gIYdJ{bPJt6))nw9_okng8BWq5V3O6;$HtDi&6YPfj>=Z!>z&7%d zj14VSBj_{w@+wRiq>7tIF#Ofo3)_{N4AwQu#^#Cl89&VqR1Cc z7}uQ3j9j%YwH~?(Uiu9*?i!E0i;B8B)DJVQ{s7WFv)=VH#a#}8cS92 zB;u@abs~!RvE7J{b@JMta#vsG>rhhCD>NQKJLcF}c*mN2-+iD#6#9s`>#X;VdQNlS z9KFk4a7tDsJ4I?ftIrbBrtkhP4SqgD?RD&+=-YA9==;^Y_|T0p4cT{6OWCrCY=h~e z!GOsjxo#Nv%Gn9*mm;6<>Oq?u)z$QC-heM16hUOjqz#D{9B3LrP_ss~{dxsTxAKw= zRK2Y#_N?T{l`m|j4pVmEDkgWxbcZD7%G->3^F^gGNx=wrkX^ya#$3H$nLu?_q0zxa z*mFsT%t<=p=FfvoT|UbEulDU(y5)9T9wt9?wzvs6!3?tqc;jk`u`iPNFB@tkL=%hqnIQB}*c%l^RNma52k z9)j4LgZX0Ax%K;PN2?o_Me#8i)91ms^=}nNt2*?y92}taxXDJoM`5qyQ{QS zuxs;peG`#1kzMMErAp+PN5g09PZs687E1~Z{qg;{lgyx`OW1x04i(tsBV+GU0sxGp zggvAJBj5a^cHm9-x39n?c}JToA}NzkO2v)$jiK!}6a#_ziHt3{3q$`>05m{5fC@ zJQME-Xmr^u;180^(m_;DF->0xJZ7pii%MObDjA(S#2m1W|kGf|0c z!A#OKhECR;fOAqFoWw)B%RIn7smKDW2rD+*C|tXc@VLwZad2kKr_M{e`91IbWu?&Y zC#r3T)9g47ooIs!cyRUJ0ciu`kt@RT_X7I}gZmRiMiIp4dUnvZGY&UL?~gc6H)~%A zyC*H9H=HHF4SC|=`(1ZKnPrV`n9~j0)Z*E@Bi5cK+u3Geyd83rp8vr^uL9SKYOTSb*Ts(F~ly2OM>9mareo6{R-AE;+Gcl zY^#j{F`Wt)MU|h;W>*#)8G`k)?V-RjV})Q_cMt_(83(rG2@Go_G6~`i@gjm2ZUMOP& z!-or=2Ir8|aA33XqSHSA;l@I4(b94wBguNr0^1cXoI;{Vb3c5o1fqPUiU>uNUUS20 z4TsG_$~Bm3FONFuqmg{Y;6DDA5Qym>u>`Uhor$Zzs4ooS`;bibcG5_~a}x^vx53nn zV>JpPKMuDJl5Z(EW8`Hy@vy@xi3W#}NMudwwshWATEkx+4o0NJeD3zVei|Q+(dB(~ zYQ1e*Hckw5YyH6Ibu~RlpVjU2bTOG=bK)8|-2J*!KJWAVpnF&-aMb>(I-Aeq=4IaJ z<$2)knZ09b2th0+%pNDNvvn;do2fbo!R1{7dnn?llw*Qi4j3gCj5{F-sF#-?Mcuml zLch|LCrh!x$8YjXc9)$=8JMTH{ACfFD9 zD`saiLPSl^45Vinu?>%xyA1m4)AX+*uK|#CW5$;ClgdUzWryTy{FMdaI1z?n41&%C zAr-#Us|F+&G;+u!It34pUt!bUXgSrg>KvGw7bx@ravKv`hztyUf;4PIM9QPzBaf*x@)pt?$S*ftT*)qI5z+m-a`Dks> zB1SV)pT1jB^sqM1BC_{T5zkR>G)0BgJ|b3PRJSR@_f~^ve^z6&ub3A_@Ssaeu7ld~ zJIVTSOb9~>mxk#}tQoq1Lu;lV496F%5ax$>akJimCN(<*r$LVbN8!HlUIfUkgVM-OFk!pq z=d`5PQ?fxKOMQ8TUoq-tbfv0$cD4y`#Fy_BL=mkKzZmWmD|zp%DBd~7F-}^|(=1s~ zR~2uW1qjnEOCz^3M6O>h;PM*lHZ!MSUnf#FnXzaR^v_d;S#E=;r}FG!PHC+`u#&JG zwV>ROf=#-DKL(B|g$7Fuo3J|_ev7bjc10lWMgSaUO6HdKRij7y1E@z?Q1)k)om=U% zILPtt1P0IC1tt+Blal!;+?=Wv%pX?VOOKvz4tuIq9y=F>7{Vhr%d)F@0tucRYdvkM zhnrVdhRYS6mO}@Pi;mXo(7&CXmN#y?+`+#Y=QjNseB$0dPtka^qCa|cr%~uUv!C=B zmGhn87PK3H&+C{)0(aYX(_;avQ7Fm^DXEj6x8J;2SfpC+)M?VpdftYR(iEIk-KDw< z=x~00_fMUm{&_~4D{zV?4)>=inuD>UqnY(5ho2o_gX)s?78{}${frO8wbpQaMFdGj z{dwV{5t2)UQ#h|8vK0!FgkQn2Ys9NZNTMl6SX-z|TuN#>vx5nHCKeL`FYl~<&9ETy zR8h3c4kze@y$)hi7N-wSL4O!?EOhPLq|A*{hk-8-u2)C&1n^-4l78v9Any{OCX1(J zOR2D;jJozsEW#jTJ-UCl%_)+)9$vV0kU}9b{QO{^gFXwzv?$ z<$8+KEOxWe|y#Xx02?F?#g1Ym0kaq;gd?YN`|=P3pEqVDyzby+78?-xx~qt2UyWggTWw zF(dZ+6G69oux+b!_=Y)&n+vRZ!!Gu?-t{@8(Bs)*uKT0j3w&~_jD9ambkF(_{Hfwz zOG?CdHnJ@{blu~PUJ;LvAEzt33j3fqAhyWG-b9v|R-%zu(AI7knj+A}L6i-Lrss`c zpN7B#VlM*ObTcNJtxZ?Ln#N|g0M?@IGl=m6`<|qoyGMm{>`^wCgyLEL)n(99cdYY8 zl7o|i(@M~tM}sLbU>}PqB6;5Gtb_k`eyAP(`Rkboy8Qkfu{Y+_vK;}_?be0mmxu|W z`IyJpk(;g24ver)ijx~=s6LTJ3+Aw4Zy(EqLIUk!uMtaR?T}8ljOl)328)lyJSge= zE`!;9RgQ)TriQirEGS@bDvvviU$E0$Gf_+~VEe^~_D0&KXxMStd<#lw6e$>CQfEvR z5Y=ffhAPIOwcLdbFC@;}%x41lmFH?y24Vca#WOg4EJ+&Y)YPN~ zPZ_~ds^1*fN#%Ar;2?#@?t$&{Fv1?)H(-1Iim$B`-e`^7A*c+v)@ZsZl9g!U+BvwD zJ6G-j1$Y{1b`N?6JiwXo%2m05FP`KjJg7JqeQ2t)BZsM=**D5f3d*9Xs}mo0X3!Rq zSRiTW)HZ(zPI=F_zS-2T8(B4*9w)};Ywm!Ey}S#eh)>btcCdQ>@^$}mFIw~ z$*f4FU06(~{8JnD+!(+C`~}(!Vr4(Amhu9lb{~h6TrT>EWU?*dt_-S!6gk-FolC=J zpFua)1WdgDp3CD7BFm*KeiX)*1oOv^ndOtQ33>LrYFB14WDHspnf{^cY{KYav*V|J zV-~v*fpw97^-NKr`Hw;%$*VM3FH3BtFBto~eE&FB=XZc)bp}rP>VTgq?Ej3_KUvy1 zIOyBE{RJrvq-e)vu%iZ_0g)0RoqGx+qM*2Qytz2p%BqrlF+hi_3A6;9WNBNd{N>Ez z3Q2r_3b{yO&!DDudTV~~9yeh)rZ^BAqEBSvOOP3#T9JUlfNf?l%+*u#e8 zV@mbZPs9^3<9BEHF;}rnM1*|RSYs&hl0vx(jC>}h`l1RZ4j^FR;%^6qkV&S%mc}6l zkb7>QFp+GHa6yObD)W-;f(|r|7HH_2-YY&vNw^3>6=CJ}^d@c(s zCLp%o0-l|;;j_!c@#oT8-9jtv;M_O2ak_(axrLYWVNC#%bZooYeUc6LNeziSS=;(^ z!uf_wi#r-#)}8|pZee#Fbk_cAOc2)Zl^k}{4P_k0N-X5oxE6O6e$56NAen1HwnL5Z zFEhKe$vwzocN>S8Z>(V6N9MGUMv=uov9SY(021SC>i10rj|1wTSsU)H8m493cn%Rh zbEzrYH^BtEW~8P-nv@-qnZ9yJV})>)p_VE!D>Y$?fL;4I?O*4c#w%Xv3~PL_wcal? zrUTqzo0yiLGR~Ser)ySmQA(rQd;6C4Ow-W2F1q$QtXFHTrQPGK5qvCSUen}hP3U3v zYvPxSkG;66ydjTAhBxD&3`|7!t{P^7Mhe3m3_P;4m5JMnL?w30XZwEAg!&S8^pL{A zh1@GNaj#s=CP-0BP-8KsoF8{!B@p&4tf<)kkqwN6K#~XTL{q z&#l(hP&*~n-tyNTCR?<1J9m6<$Qj>(p!z|frH1)D?Wl86HDH->?xmRG;RKIqg^S=Y zN+p6OK&NWV5Q!lOrQqEHIFCqq>f6>0{1VkR2>FWl#0qy#)M3{cE%$_)1W|m@yp*@U z{5qoX3iD5c3QO-WNF?B(q8s*~TI*j&7gjNg?1-=4UtS;*r-$PMNjpj`2gkoyHEMkf zn)7lt0R1-PY{XTwT-{xZg3^>xo&)eEn&M`jjvQv*I^WoW8Rvo^Mnm)5eCN_NPsELq zdbu+2WS)G!f7mTWbQ;%w{XtlNt{8f_N)s}cl73wFKb=1C*nQ0M2d+dv{G za_GJ{%h02-!K$rCjHKD)c^ObDgffchie867UD>XzQK_zl+gs^*J#T z)6XLXh(tsa{Xq>{VPY?b?5>0NkDuT@=QSIY^ziSUov_?IaRlyz2}ei9t#JF(^C6N` zxKY=J5;EOP6BY;`TJ`84ZQW2r7F_I7Lm0c)71Xv~tQ=1x-G|W48pIB@;)3Irn|kl1 zT?}Npl)Yu%sm!3rypLD*nco?%yV%3SKrZA61T|jplRRA5pP zSL1x`Xer5X68al?cZ{Gi)aQIg6SwFKzo25C=sg8NdZU7D3|*`MY&taoIObvx8;u`qoyFI8l2sIh$Dn;-sXA;f_{QX%sqI-p_toC zo6)ElR;&%t`mrdn_;{wB%)ES~);jos=vp0sQ2dhnLr%ne2K*v`&|9s6INuiiYx@&? z+R&B!muS)m`IWTlI6ZvYC96I{iQr14XP>VxwlmrAnsF3ZlZR{`QLx|}daE`=h&qb7BMv#BJ~EM&Or&TM5e)h6bXkVi*zPb?`B4OWE8D`_gw;*;Oq zE|Z?03hys;dXt3f!y}$iOvW=@8;stNe+TtlrKv5@g%2{)ITg` zZbb*8NPx9N|DLdk{(!EccE&(I=sQMYT9%)4r>RkHg7*GRR>j%~F?j^G`mvE6iBu*H zV;#?gOPb;%8#1UaI!_ zu|!W#?skEG#VzN{!Y9!;H=9S1Oul7@Pv_cq8Dl@H*yuo4ABc8namy~NcH5xg4QX6w z6qFjKhHA6bnUytK(r4sFVb6;}^n<2fxo@s-A%+>BI#2p4NNtz(q!8gm#AKE!foR$R zuun?o28D8fUGD+H9|p;sHtu#I&stO#CqQ|%f_Ft3_Q6*OR}TS%jL2t@F`9ZvO??p; zX@fn1RZJLtxS9!_P$L)7xBX5JGQ z)W<3U#k5-W3UMm`w`gE~Z;pN_>mKs$lK8MLIiXxb0BUrT+K92dcO9C-BeZ-bPwNjY zZYG~w6V2iv=*ejif?VYQO2+R6(L9`zcMS?|ZIdxRwdz%JVLS>QeP*DqnA{^q&ab6o z{1LPGFtIr|yQL@tOm^ zRnreQvv~PNqQr6~uT{SEREp56{c($t6wb!dPRcpn);aLn*BePo=nuO;s`T5F$QO?> zG7p~dkGsTD5AzW{7KxiRmZr2iE!rQ@%5q+^m1laVsrJu5p|L!uzr%Z}@Q?OU5dgpH zrV%fQll-xJSA)sFGGteRG-($KH)&@IHEAdQZqkn8&7>U|XZlvCA2)J^V5u z94=bMTzOkIxxIF4wJv={;x>`qGV4?@iN)LxI1cS`Z<&oyf@Z*%srn1FNt%Gwwi?A4 z(R>yiSW!ihi0|Xf0X(|piV#EkbL#7pg{aCf)jiviTyA(367@b%MROn>MUxG{THG)8 z=mpcm!J6EU{rm$V~HczH*4mmOb4vacL=D4i|N9w z)gOrLb2Vtc0M6m9W=aMccacUdCP|PN(m!Y!G@GYakejNq9HkwYt2Bgvi**lk9@|vr z$i=H-Q-O`Ci2|V$NlqBe!IMJlb#dn`Uy+D=U~9zHsfDv>saXB={>!1VxWC2_lV_ep zzp9PY=ThVEb9!G2BMl$=NH{keMvSm5nzrvErQ9W6rRub5v{D-tKxchQs_AVZt-ssb z2Amp_DPVVdJ;X8~K{zspmWM25tP9ZHFe$pg!kufO2T6~l0^)jnp4aPrz9^kb)B4YS z$ouvg2@jvC21bBkB#{&gDefzaVu8OAENchf{gQMYOHRH1r95{+KlKo}E5I>5KKw@~7bZdurfVnV)U6nkGK-1>+x}uG zxaD(7|BE7Qe8*>GmL@-()dleAvRVwrafc1=pu@?0bL+``>$CGg-NA&~iQV$lDo_hT zb%byHb##drR1ywj=s5WL0dO&GoqN#GM?rVEc;NM+sPsuvjh3?I@R(p%{B~{7`H>is8#v5@wv;U$UDFXz`oPMl_)n| zxL%tY!AUb?LRMw+jenTZ}!)_`i8WXOYw^Mw!Cwb@KJ9cLzG##t$>rEj5Oi? z%bMc%G!Atu_RLb&YWOz|fyVfE4J@2iYR(56%;TRmKsQRLy}wy-i=E>+iOepxG%i%} zYiKqFt;?&TvKAm`2IcZ(g#`go9@yh4Vla@nGltkFR$cP*y=+oF`aW!II~=5El8ax zu{g8TC>)dWl%p~SSsBZrs}f^T{GX`ViP!7U8itLjP0tc42?xnF;ZolLlNWhz7+(O#z4S345ir!{mFU5|nIR`nI$4+5=F6Hl-C9{i(FK_X4_7w$eK} z)Z124H6vX6;f_nl6PyTbb=6i-!My=h1s0C#x`}zgBznt9CNx{xA#SQoO%wVUE<2{< zh_|+?CQY#V5H350{YN~?4=v6@@yUU5jmH>!5d*-Q_wZOQF z@|T4AjP)IJ-wPL3VYhbdyk{-A5T=T9qBlq8;yrEd>%PYW_rT~5Z8&-Fs^A2!-!EY! zcWA*eyjBHM*r*C6W|#L;VV(RG!AAb&W`G@Y2h_q1^TZHZhh@?UrUAH-WFk5a&0zfU z_v4EWjYde^s!}tmjnj7J@0ZN11$EC_p}bc1$#R|#2G;S8M00tLMITzRn>EZG8oT9I zfO!TcECvRJvZ`AuQdw0)mC4ILgGH?g0OD6%K5Ju!fYk(Gj!cSH2n}6zK`>D_BcU2s}iyt}yCAXdbBo36z_=kL0CxsEZ zf_-JtS4W!Rp+61ZiSUQ>Vc)o8*=miP^&ii(LsC7ZtW20kjESbs(D64M! zz`s%I&0sL@jE8u0+m;&fZhWjWL$1b7DMTUkm9k6l-X z;!cLuW5gxG^knm-$E3+)nn4-FE&N2MD!^ijYIf{0SLVa?MO)68gECUqS*W3pQwNFc z*|UPx+!Ic9n1!0P4odNd>}p*U4D-18o5gl+?t3F6Yi~>Bo`X=Vhm26hj2l);Xh&~# zWH$QrB^$R{t=36*-Aoc^!5ubqm1!P_K(m|Y%^Nt^-Gjli{IQEzLos2c+U8k}hxXtv zoQ7R9qjmXHWNFTao{iGy{#vlH}7qrwq zv?-_AL!MPZ#kZ%Ysl(bGOMKMX%li$}6NqPEkr_#p-Oj@j4|E}iizuHs=eE?OTryyC zgKr(DF?HsL+N9Q&3x0HItkb_5re zLh+h9zQciYNcKx8X;?x%ez9V0F;oxjqtu@f=)FZ;u($JKwK0LK&?ORmWHKsfVW_Xu zQ;xieydtEoA%?957o(TDRuN8rglq2eu{`n<){H2bU)*om+v3iy1KpyRuc2uSghmZRDz21O*t_wCtL#ni<>wE0sk;EQ1o6MSi!kO-~FL4HS6 z#lt(Y7vBpkhNPOKPs@0)vT534<=#`*+ok}!S5;{Uxh0)}`&~6LYz6mWXsWU@0+(ko z?wxQ7Dx|V1$QSI)%$81>C##)fv#ptkMxroTInfY8i;e4oPH7>WzAEjHv<1G{nlotq zoaW`-dO@$NhA2H(4K$IxyE_kYWY5z)TAu&7C~IOkRrv?-eM|sX(;)xneV70p7?ku4 zERBC!5tkFDqca!~ht857;gipBU?1u!HR=?WqLfMxa~FAi4W(<)mqxY%w@8o)%ieV{KRBZC-ME|3uRly_z~%hnQ;XY|~R6_>-)fwjG@lrTk}( zQB2L*H(RShwaSHo&(?ziQd@@Jn??b`7C9=kTBa)=rddwLM-{Ok3-#9SpLfT!7!NkV z3zBTnEq6XGOvuR#>${I_N`9#J-C^N(233P{OCM50hy-IE##NTxs;I(Mj%NR%G65-v zPexStP#Apo9VVlNRP z67K1~vFkt~w+K%IBKpFD3AICPcvQTYi>lQ3>X`0R@XPrM+%;)6mg)cW(%Vvc`q(p;pb zR0$y`(_Hq!OQi9n*3ktw_EuJt6W$(}Z`cbRX+7<=A?pPFAj?fn>-QXw_h7~P!G=A) z7LBu3n9ra2N+P;tBx@V8-3~12>E^PVooNF6q4c)V$3d`9%X0<_3%%yyA7KUAVre8P z5LOsCfq-ECr@H)Z%=v>_bYy8NE=eK}uQi)@2aJ7eM+jrY*eZbMcn1dm6$*-+EMu>h zWvE^pyPEFJ?cCXfIFq3UNx4|3Qkg&f;hI+4k4iJ`iZyQ^$<1|)X?vo%gxT$vRf=^p z3X-_h>`3or?XHX{{|-H~0fV&5gjwckv2LHor!{Yn=TwYo#%a)Z;#V;eu1p$egO6LT znIY7lYo0r`u9Uo56V>~mZ`j%i$Z0$%=FbGvD%6S4iO~nITzzb8ILjE-3BtALL`L|= zYouK@FrVy(3`G*nx{!}La(QlZdB$ioJ~3^(Re@&>pwa6SujrA5fFy=8B(!Rwp8@m^ z@v#z_vX9eLC7$21_r1A&qlbM-j%%pGJT&%YHxzO>&6=IKj@>(}7DPTw@R)$*WyYtW zb#N@7SYyLBDrRUg8O?I=GD&8;Em z80jAp9<&eyy5&Hh3cjL^o-^y#G11w-u(eMfHs5ev?I{$A;B$K$xls&c9Tu5WBb9wG zpJTxwxqQWU+8MrqqMVUOz>M2CjX5~0A7HEDy7K#RJWwPug#thBEbmU=@^F*32|mpUiVv% zY$!~Mt;a^!y$z+jnODMEVHBICjrwiKNIzOP4`MRK=(B$~nO(}i2vCeGb@H-*PUi=5 zi%rUHi>#He%|LGPPzGnv=|ri64dfPPdI^gtR=3nsj^PMo)ksB86a!sNBbBpF%;Tzt zA2f5ZIbF+x;n&J?^PA00A64M+x|N0Mo6Se`UbErCSQI=!>Uk`|Dv76<8{BdeA= zx}lvANyF!f^l;w#C3d|c(olBbQ4+-f*1-P`VDlqZ&>EE`Xs>_lEIMWD#`2fb% zPh`U=V%IOt*m%Loo;KS zq`4cI`hKdTFcKW9hyi=uBG-LRC<-VpgOtq<6ZMdEbRFrLde$BPmeer!!tA<4BIo)&p;y!TC@SIMo-C3wC#zUbKJ1EWOJhD zkKggSLr;oSBV^IipqhADYJIFD*H3Jr0mK$$f;t1wpOJ70D@~izv<{zSj5M?dWPHC4 zml*a9ItS(3Uw-T&JW4l_zl~TArHC-342!PpJhqM8xoxy)pP0zP4QRK;sgJET!o8Sa zBx|lLOW6ZhVO6)=w9OvX>|xk-Q~5ubJ2b2fN#8kZu)TXq{(cI+wClOJJpIMQxt~Oy zOh#;D5_K-%b%-plITFDIr@Ml*i6+KfqmQk)omN+6R5N*)hgLtM{&TWuv1YQi`~5^+ z$TxoW)9`GWs6EK8tttxOa(lKVG;y5qCPePj@I2a5uwwm^X>|Ux>$%7!pm}lm789+N z5|g8qveRit)Dh~K>&N@+&p)|^N5*g5BH|~vs8&!JK570px1jwOw+H}oi>A6r4}6be zoDX>}t9B&rv~b=?OugKtl5Wggb;)J--!GXs3MtcrM11rFwkF=cMd%*zQH$9L2dGWA46Va?_2m zAw3q7b2HQrs`!&zi2TAW)~4Ej;}$-SrCk_(nauCb`@53YQ^)jGN0yRvM!1fDa*GO1 zg!Z4@!Z{#BTVj5{u9ZUMB-+n%ZipRG^F;1DN^)GU{y_Em-!b)R8iex;6%T3kL=nhKg0L37$v}^0w?IU z1_wwdD42m{BIuAzPa6(6l?TW40V0NGAiJ0bFfbYbuPx9~^pg8p7`fYlTxN`!$?7*tEtpA&o(A#f-*k5o*nNh=kaK~SmhEhuTqF>dTXd0w{wr@H)4fWf$wTO0uhcdl>pUiWy^b4(_ZdcS9 z_!}7W#I51VVO_H(xApABE;)xHpEoOQ+LUG9Z<@f<;i~f|4a*v8PbK8|5G!6699wv` zH5*M~^%XZH8_~XXYwwtry}~U{$|&t(nCgBwzR%T|i%S8qY^o~0Hts4rX)-CVUkif2 zkLJY@aGa7a#8QRe`zoQQilFR+Zg(@naMrs2nl{T$V>tnfZ01tztRz_4O+7ojyFWUD$RsLtkRNxFmwE#Fwr;Wdb2KOkgs1 zHP@eCe*iWyX&vD$w8Sk^d!il&*g`CKt;RQNxk2nFxVlw0)!E5@1^UVV_h10;Z!?Sk zB?SN92!T@!x%_*e5kDTd+X2qsUd&%m!FIy9_0muG_IHm6Z|`a;9b#+G1B%`TU3th@-68`0y6TR_K0QAh-CG$op7 z#u7{)WxSKxVTODM=P#49JT2V1K zu~oiQl-`=%z8kE$!a)B)LBdDcLGn|aDOO_y*OGD@J1O7Ng~Ki?aB}ByNn!4Qla-fVBq(DmuhE+Iiz+S ziAqh+QHMK}mc>!mvEEUg2E*Tq6F4$l%-ctjs9TeBZOjAf@+f9jV*D8ZdnlEMNoMOl zgy(dwd9^cNSsQJI8gJ{5jO$P8-;dV`8`FXsf{B3K_co~Vu|KIns-NYXkG`aJW@K0} z77TI!^5;_joOsr(FF6#&D4g&v@xf6YCh)?(jyPr*RjJ8`uHk~zZ`y=Oc&a~M@HjAC zuNLxKGu8&c(E_Q=muI_9GHufS)ANDtgoi!e_z;`Hqo$b1X(Vi7Zu?D(Z+5QRnRn|S z=BE6p$v+pO$9iH|T^g}6SF^{@VR15w09nD70FV{nZ9{h2gOOYfURSe!nHIg2v9|be z7#P@k&qubcxJ4^W@EY5}?HEhHOMm%THKcl(m%29I?iJIqEg+b`a6u6NonG(eAAv%; z5@=Zp*X4WB(g|0(#^BuO}d|*?GpV{Dh)CYckIS$z3s3 zIl{?|TVk#Z@v)(^_bN*$R4RBlc zA>w9g@AWhDh>EO(XpM2&-f~aWF{&U^6#H#zq%KZ{-bzk!7QWcmA=w5C_`k*DCIj`4 zu>>i89t|$Kj8GyzrpXjW7>r(ZpY>B#hAWc~7T!8=ZIdhnd2V)=7@qEZnkjH8*t*;lh6XR>FnoT zGPSkqPRVghjF3)}Q4&eT@1&4U*EPEj==_i`xdnZ;z||4fmL!v6nhMG9x*#-d{_0b- z^K)6w-+Qe3z-$tGVAUN1R$atDS6zKu+n?a{e`_wV*nmf-!f%_NbdX#i$e(_t40#Lq z2H7Zx>#Ag)^J#pQ6g}GNyEWjIC!+{X#Fn@?>-);=@qlf&g-p{RVq<;1CpXn4Y^9^* zl6!J@N{ni=8+t;GB<3f(41uBH#6>H$xYbIg?;2 z#leIowUo`mV8}nvE)G;h81&hu%up$*dLvGF3?8t(s48)Me~2L1SxvwXLDsYp7JOZ_ zN{_QIeaBWK*G4K`s=4hVi~y3HH#9Qb^Z51~9Yx=Vo=522C`dsv=-3#TbgusM_0CxZ z((vSNQ!Fh<<*`+G?|N&3g$KwPIO@cVoVm{!JyS&77E3q>Fihyec!4(2r{G}Jl(16= z?mAE?NDCj{ILy22OGLw=9e547=(dSq4A||EyE$)b6r1b>9d|fVK7aQ^t1TJ%p{Gx{ z9`-8Eki~nlhz&5?b;*Z0&-Y39`1Q+^fvbsLF4L;*m9!{0WQ_*ML`F5`a)gMd%~ zdz1gpf&5oZzj_S+q00g2m++7N!@sKd)!*t56|O)_)^95QygvF@@n8KQ{tzd?`nUM+ zApOt&@W1N$b%pjHdM0sy)$`xw=hp?Af9OFc`c=<=v0U@lto^!_@eh@J#Q!Ow-xoIi zD*UTu@DE{HpcD9S#rw-F;8zX5+Oq!8(8=&`4SyM~{wn{gapMp9W2RqA i-JiwpMgAoI?|x5C>K(9c{~XRCfp`O5339}Lp8X$RBl+I| literal 0 HcmV?d00001 diff --git a/docs/images/word/documentassembler/inline-HTML-in-Word-example.png b/docs/images/word/documentassembler/inline-HTML-in-Word-example.png new file mode 100644 index 0000000000000000000000000000000000000000..422f0dd2b23268fa2ecbf6099a456cad95cfc64d GIT binary patch literal 1874 zcmV-Y2d(&tP);M1&M@L7)!^1Q*G_|$0r>CcBX=!?TdRJFhn3$MKN=j#E zXCfjZm6eqf6B8+L2@C)L2FpoAK~#9!?V1U4>?jOC#U=p;?Ah*r)sbWz9CwoGCG%(b zysEfpV;OisG^qfS$z(E_OeT}bWHOmdCX*jTBqn?KCRVa=FFuaKE$9*6VTl^wCUX2C zkmqZq$^%dmU^PT*(no9P#Uw$@LL87t;nrewP)z@HCo!j#e8}(urM(T0LW6tx+SI~E z-mwuG{nc&&ZXcncg+H6fILH*DVP#w!xSD^8m{PN7x~a%;5BV`mOo$Jd(kC6gGONZ& zi`fj^%h#rE$YjWguvM**&N5_E{+M!BJXU!!BS4iKiGsNB)J=h)rnY@TJ!%T7;foJs zip&gdB)UEVPyCQ?ff~w1s#l)cT3rko2XV~d*sM+kmx(I(mLtrf2FZBo;j&UFRxW$| zXoo{WMQuyRlSo^V2f7KfN%M|^BzfiORJhr*DG!PhMT}87F;cOVgnWh2e3ME*6v*ouYNFs+!K`-(eX}ra? zw`0QwHdk#c9j0nT`5h{($!YopZY1P|tu;=>o_$RUta{NWHH539n6i>%DUR%OA`vk^ zXG;1Nio8C9BP%OTI+<$FFUHHGj8tc-HYZb?;h!L*@%+ldpDou~h`#3-PlWwqDM@HA z#Qu(SwAIV&ieNn`nZt~PR@xjjq|CRR3Su}Kec(cN;QQ$PZc0+G!fr}Q62#AM{uxt+ zSWwyv8-a4pJ{Xsclp%62EIf+5rc`7!C4`l|W2D?Hi#tZcQRA`;rcDbgDcXX*OiVdL zanmHWjtE&&l^#|vZISIO{8(ZaSvIb73eW_E_t{r72+x?Rrkh&sFa^r3DMv~LcC=Le z7Ia$bjx_1Wu&1nTsi{1|M*kD(X(xaR#aAB&tT&DHjTlVgqaZ z)f820bC$IZ!G?;sUQF>i)nDG{$tu&xA6ro3;hy(X6th;1{nU-A$TWG|_PD9BN5AY6 zG}EarS5sP@$0kCTqkTP=7#W}L5>~hG5;U6(g?5{LK5#o4pTI>9!Nb(pCFJE;bC^Ow zt;xNtw=R41TrPX`izy)j=3pN-^;0uVO(v7cWHOmdCX>lzGMP*!lm7t(llz#a?q!;~ zm*-5yf98J^9^qs3T*4>k&3qg;ZXkb*Cz9fU0`B*SW9js0F%O+F8>ZZYG4~T4e25JT zd_pJypPV=I@p9v1Q!b@kQ?fw&y+jvNzY(+52S+WY(sR@C4k*Ec*pE!yb0Te+Vxreu zMkiGc7gN6ylfHc0&efDBW|!}>o&h`lp&KgzQtc;s^uSo~YE~{u0-_!z>xB`?qblKd z?NN_nMs16dW_6r81KRinN#=~6F4C82E2CR#D$zLNrvIbP%jc$iZqn&?G4;!{rrzdM zqPSoUk{k_f&D>)>;KMZm7|Xs~C}&eKaUbj`^hiBMmJle7C?QwCb=?;u9z3>?_ZAc7 z#D(34&#aehH#NG|O^qXNEnC>g?xt!I@;g(BD^2kiQ-9>wO`%+@8Um!na=C6HL0f@w zGVqd4rnHyFkHQhjqusM_Lf@3K!{vAz{Xq>qQGBMM9f7mP=d zXQ2d5vwo?o8cv{7HZoz`yEK~XrpD0`M~aueeVl##NGP|a_~%R{ljF-S>bbyO~pN~S1JWTO}>4@q_ z*i2DNj_i3s&pqCnFpj2QOzDZVv#EYI?u%W**_85vsh8+!`}*E>t?&c+5168dfWM2s z@6B(#YU(ra@xOXr;kC~GOiWYvGEGe;lgVTc39*(@Og{l<6aWAK M07*qoM6N<$f+3rK5C8xG literal 0 HcmV?d00001 diff --git a/docs/tutorials/word/DocumentAssembler_InlineHtmlSupport.md b/docs/tutorials/word/DocumentAssembler_InlineHtmlSupport.md new file mode 100644 index 00000000..5adeb096 --- /dev/null +++ b/docs/tutorials/word/DocumentAssembler_InlineHtmlSupport.md @@ -0,0 +1,64 @@ +--- +uid: Tutorial.Word.DocumentAssembler.InlineHtmlSupport +--- + +# Inline HTML Support + +## Introduction + +Document Assembler now supports basic inline HTML tags and if these are found in your Content select then formatting will be placed directly on the run. + +For example assuming you had an XML file with an HTML fragment such as: + +```xml + + +

Document Assembler is AWESOME!

+

It now supports simple inline HTML.

+
+
+``` + +And you had a `Content` tag in your template: + +```xml + +``` + +Then Document Assembler would render this in Word as: + +![Example Word output for Inline HTML fragment](../../images/word/documentassembler/inline-HTML-in-Word-example.png) + +## Supported HTML tags + +Currently the following HTMl tags are supported. + +### Block Tags + +Both `div` and `p` tags are supported for block level content. When Document Assembler finds either of these then it will treat them as an encapsulating paragraph. + +### Inline Tags + +* Either `b` or `strong` are supported for Bold +* Either `i` or `em` are support for Italic +* `u` is translated to Underline +* `a` will create a clickable Hyperlink in Word +* `br` forces a new line + +## HTML Parsing + +HTML parsing is provided using the HTML Agility Pack and is fairly forgiving. If you have HTML elements in use that are not supported Document Assembler will simply ignore them and process the rest of your content. + +## Usage in Templates + +Inline HTML formatting is supported by default, you do not need to change your `Content` elements, you just need to pass valid HTML rather than text to them in the `Select` attribute. + +## Future Developments + +Inline HTML support is in it's infancy but it would make sense to add support for: + +* Ordered lists `ol` +* Unordered lists `ul` +* Superscript `sup` +* Subscript `sup` +* Strike-through `s` \ No newline at end of file diff --git a/docs/tutorials/word/toc.yml b/docs/tutorials/word/toc.yml index e013d9a4..b223bd8f 100644 --- a/docs/tutorials/word/toc.yml +++ b/docs/tutorials/word/toc.yml @@ -7,4 +7,6 @@ - name: DocumentAssembler items: - name: Images Support - href: DocumentAssembler_ImagesSupport.md \ No newline at end of file + href: DocumentAssembler_ImagesSupport.md + - name: Inline HTML Support + href: DocumentAssembler_InlineHtmlSupport.md \ No newline at end of file