|
@@ -0,0 +1,123 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Text.RegularExpressions;
|
|
|
+using HtmlAgilityPack;
|
|
|
+
|
|
|
+namespace InABox.Core
|
|
|
+{
|
|
|
+ public static class HTMLUtils
|
|
|
+ {
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Takes a HTML Snippet and converts it to full HTML.
|
|
|
+ /// This is used to convert Fast-Reports "html-enabled" text to Syncfusion RichTextEditor html.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="text">The html snippet (no <head> or <body> tags, paragraphs represented by line breaks</param>
|
|
|
+ /// <returns>Properly formed HTM suitable for editing</returns>
|
|
|
+ public static string TextToHtml(string text)
|
|
|
+ {
|
|
|
+ var result = text;
|
|
|
+ if (!result.Contains("<html>"))
|
|
|
+ {
|
|
|
+ var lines = result.Split("\n").Select(l => $"<p>{(string.IsNullOrWhiteSpace(l) ? " " : l)}</p>").ToList();
|
|
|
+ while (string.Equals(lines.LastOrDefault(),$"<p> </p>"))
|
|
|
+ lines.RemoveAt(lines.Count-1);
|
|
|
+ result = $"<html><body>{string.Join("\n",lines)}</body></html>";
|
|
|
+ }
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Converts HTML data to Fast-Reports compatible "html-enabled" text
|
|
|
+ /// Will dump tags that Fast-Reports cannot display
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="html">The HTML data to be processed</param>
|
|
|
+ /// <returns>Text Suitable for printing using Fast-Reports</returns>
|
|
|
+ public static string HtmlToText(string html)
|
|
|
+ {
|
|
|
+ Dictionary<string, string> _replacements = new Dictionary<string, string>()
|
|
|
+ {
|
|
|
+ { "\n", "" },
|
|
|
+ { "\r", "" },
|
|
|
+ { "<p[^>]*>", "" },
|
|
|
+ { "</p[^>]*>", "\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<HtmlNode> 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|