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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 >7RaKptbZlzRk%~Yay{>
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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
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