using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using HtmlAgilityPack; namespace InABox.Core { public static class HTMLUtils { /// /// Takes a HTML Snippet and converts it to full HTML. /// This is used to convert Fast-Reports "html-enabled" text to Syncfusion RichTextEditor html. /// /// The html snippet (no or tags, paragraphs represented by line breaks /// Properly formed HTM suitable for editing public static string TextToHtml(string text) { var result = text; if (!result.Contains("")) { var lines = result.Split("\n").Select(l => $"

{(string.IsNullOrWhiteSpace(l) ? " " : l)}

").ToList(); while (string.Equals(lines.LastOrDefault(),$"

 

")) lines.RemoveAt(lines.Count-1); result = $"{string.Join("\n",lines)}"; } var doc = new HtmlDocument(); doc.LoadHtml(result); HtmlNode? head = null; var htmlnode = doc.DocumentNode.SelectSingleNode("//html"); if (!htmlnode.ChildNodes.Any(x => string.Equals(x.Name, "head", StringComparison.CurrentCultureIgnoreCase))) { head = doc.CreateElement("head"); htmlnode.PrependChild(head); } head = doc.DocumentNode.SelectSingleNode("//head"); if (head != null) { HtmlNode? style = null; if (!head.ChildNodes.Any(x => string.Equals(x.Name, "style", StringComparison.CurrentCultureIgnoreCase))) { style = doc.CreateElement("style"); head.PrependChild(style); } else style = head.ChildNodes["style"]; style.InnerHtml = "p { margin: 0px 0px 0px 0px; }"; } return doc.DocumentNode.OuterHtml; } /// /// Converts HTML data to Fast-Reports compatible "html-enabled" text /// Will dump tags that Fast-Reports cannot display /// /// The HTML data to be processed /// Text Suitable for printing using Fast-Reports public static string HtmlToText(string html) { Dictionary _replacements = new Dictionary() { { "\n", "" }, { "\r", "" }, { "]*>", "" }, { "]*>", "\r\n" }, }; var doc = new HtmlDocument(); doc.LoadHtml(html); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "font-style","italic", "i")); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "text-decoration","underline", "u")); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "font-weight","bold", "b")); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "font-size","(.*?)", "")); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "font-family","(.*?)", "")); ProcessNode(doc.DocumentNode, (n) => ReplaceAttribute(n, "background","(.*?)", "")); string result = doc.DocumentNode.SelectSingleNode("//body").InnerHtml; foreach (var _key in _replacements.Keys) result = Regex.Replace(result, _key, _replacements[_key]); var lines = result.Split("\r\n").ToList(); while (string.IsNullOrWhiteSpace(lines.LastOrDefault())) lines.RemoveAt(lines.Count-1); return string.Join("\r\n", lines); } private static void ProcessNode(HtmlNode node, Action action) { if (node.Name.ToLower() == "span") action(node); foreach (var _child in node.ChildNodes) ProcessNode(_child,action); } private static void ReplaceAttribute(HtmlNode node, string attribute, string value, string tag) { var _value = $"{attribute}:{value};"; var _style = node.Attributes.Contains("style") ? node.Attributes["style"] : null; if (_style != null) { var _oldvalue = _style.Value; _style.Value = Regex.Replace(_style.Value, _value, ""); if (!string.Equals(_style.Value, _oldvalue)) { if (string.IsNullOrWhiteSpace(_style.Value)) node.Attributes.Remove("style"); if (!string.IsNullOrWhiteSpace(tag)) { var text = node.OuterHtml; node.Name = tag; node.Attributes.RemoveAll(); node.InnerHtml = text; } } } } } }