我在从 HTML 创建电子邮件并让它们在 Outlook 中正确显示时遇到了一个小问题。
现在我们都知道 Outlook 的 HTML 呈现有些差。 (客气一点!)基本上,最好的方法是将样式融入 HTML 元素中。但是,我想获取现有的基于网络的报告并将其通过电子邮件发送出去。显然,报告是使用来自 CSS 类的样式正确完成的。
所以我需要做的是解析 HTML 页面和 CSS 并构建一个扁平化的 HTML 片段,我可以将其邮寄出去。
使用 JavaScript 和 jQuery 这会相对简单,类似于
$("td.baddata").attr("style", "color:red");
当您遍历 CSS 时,显然会更改选择器和样式值。
在研究这个问题时,我偶然发现了 Html Agility Pack ,但是我在文档方面找不到太多,所以不知道你是否可以做上面那样的事情,并且宁愿不花时间去学习它是如何工作的,只是为了发现它不适合工作.例如,它可以根据上面的选择器返回元素集合吗? (大概我需要一个 DOM 选择器到 XPath 转换器?)
最佳答案
所以在四处寻找了一段时间并没有找到任何明显可以满足我的要求之后,我选择了 HTML Agility Pack。并制作了我自己的“CSS 到 HTML 扁平化器”。它采用原始样式文本并将其应用于 HTML 正文。
请注意,这是非常未经测试的,使用了各种肮脏的东西,而且可能有更好的方法来做很多事情。它也不能处理很多 CSS,尤其是“:selectors”,但它目前可以满足我的需要,所以我很高兴。
public static string FlattenCssIntoHtml(string html)
{
Regex styleReg = new Regex(@"(.*?)\{(.*?)\}$", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant);
Regex cssReg = new Regex(@"(.*?):(.*?)$", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant);
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
IEnumerable<HtmlNode> styleNode = doc.DocumentNode.SelectNodes("html[1]/head[1]/style[1]");
// replace \r's as RegEx only matches \n to newline.
foreach (Match match in styleReg.Matches(styleNode.First().InnerText.Replace("\r", "")))
{
if (match.Groups.Count == 3)
{
List<HtmlNode> toSet = FindNodes(doc.DocumentNode.SelectSingleNode("html[1]/body[1]"), match.Groups[1].Value.Trim().Split(' '), 0);
foreach (Match cssMatch in cssReg.Matches(match.Groups[2].Value))
{
foreach (HtmlNode thisNode in toSet)
{
AddStyle(thisNode, cssMatch.Groups[1].Value.Trim(), cssMatch.Groups[2].Value.Trim());
}
}
}
}
return doc.DocumentNode.OuterHtml;
}
private static List<HtmlNode> FindNodes(HtmlNode topNode, string[] toParse, int index)
{
IEnumerable<HtmlNode> myList;
string selector = toParse[index];
string elementName = "";
string valueName = "";
int valueType = 0;
int idx = selector.IndexOfAny(new[] { '.', '#' });
if (idx > -1)
{
elementName = selector.Substring(0, idx);
valueName = selector.Substring(idx + 1);
valueType = selector.Substring(idx, 1) == "." ? 1 : 2;
}
else
{
elementName = selector;
}
switch (valueType)
{
case 0:
myList = topNode.Descendants().Where(x => x.Name == elementName);
break;
case 1:
myList = topNode.Descendants().Where(x => (elementName == "" || x.Name == elementName) && x.Attributes.Contains("class") && x.Attributes["class"].Value.Split().Contains(valueName));
break;
case 2:
myList = topNode.Descendants().Where(x => (elementName == "" || x.Name == elementName) && x.Id == valueName);
break;
default:
throw new NotImplementedException();
}
if (index == toParse.Length - 1)
{
return new List<HtmlNode>(myList);
}
List<HtmlNode> toReturn = new List<HtmlNode>();
foreach (HtmlNode aNode in myList)
{
toReturn.AddRange(FindNodes(aNode, toParse, index + 1));
}
return toReturn;
}
private static void AddStyle(HtmlNode dest, string styleName, string styleValue)
{
if (!dest.Attributes.Contains("style"))
{
dest.Attributes.Add("style", styleName + ":" + styleValue);
}
else
{
HtmlAttribute attr = dest.Attributes["style"];
if (!attr.Value.Split().Contains(styleName))
{
attr.Value = attr.Value + " " + styleName + ":" + styleValue;
}
}
}
关于c# - 使用带有 MVC 的 C# 将 CSS 扁平化为 HTML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21533536/