使用C#w/MVC将CSS展平为HTML
我有一个小问题,从HTML创建电子邮件,并让它们在Outlook中正确显示 现在我们都知道Outlook的HTML呈现有点糟糕。(礼貌地说!)基本上最好的方法是将样式烘焙到HTML元素中。然而,我想把现有的基于网络的报告,并通过电子邮件发送出去。显然,报告使用CSS类的样式完成得很好 所以我需要做的是解析HTML页面和CSS,并构建一个扁平的HTML,我可以将其发送出去 使用JavaScript和jQuery这将是相对简单的,类似于使用C#w/MVC将CSS展平为HTML,c#,html,css,C#,Html,Css,我有一个小问题,从HTML创建电子邮件,并让它们在Outlook中正确显示 现在我们都知道Outlook的HTML呈现有点糟糕。(礼貌地说!)基本上最好的方法是将样式烘焙到HTML元素中。然而,我想把现有的基于网络的报告,并通过电子邮件发送出去。显然,报告使用CSS类的样式完成得很好 所以我需要做的是解析HTML页面和CSS,并构建一个扁平的HTML,我可以将其发送出去 使用JavaScript和jQuery这将是相对简单的,类似于 $("td.baddata").attr("style", "
$("td.baddata").attr("style", "color:red");
在这里,当您遍历CSS时,很明显会更改选择器和样式值
在研究这个问题的过程中,我偶然发现了,但是我在文档方面找不到太多东西,所以我不知道你是否可以做如上所述的事情,我宁愿不花时间学习它是如何工作的,结果却发现它不适合这份工作。例如,它是否可以基于上述选择器返回元素集合?(想必我需要一个DOM选择器来进行XPath翻译?因此,在摸索了一段时间后,没有找到任何明显可以满足我需要的东西,我拿起并制作了自己的“CSS到HTML扁平化器”。它接受原始样式文本并将其应用于HTML正文 请不要这样做,这是非常未经测试的,使用了各种各样的污点,而且可能有更好的方法来做很多事情。它也不能处理很多CSS,尤其是“:选择器”,但它目前可以满足我的需要,所以我很高兴
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;
}
}
}
publicstaticstringslattcssintohtml(stringhtml)
{
Regex styleReg=newregex(@“(.*?\{(.*?\}$”,RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant);
Regex cssReg=new Regex(@“(.*?:(.*?$”,RegexOptions.Singleline|RegexOptions.Multiline|RegexOptions.CultureInvariant);
HtmlDocument doc=新的HtmlDocument();
doc.LoadHtml(html);
IEnumerable styleNode=doc.DocumentNode.SelectNodes(“html[1]/head[1]/style[1]”);
//替换\r,因为正则表达式仅与换行符匹配。
foreach(在styleReg.Matches(styleNode.First().InnerText.Replace(“\r”,”)中匹配)
{
if(match.Groups.Count==3)
{
List-toSet=FindNodes(doc.DocumentNode.SelectSingleNode(“html[1]/body[1]”),match.Groups[1]。Value.Trim().Split(“”),0);
foreach(匹配cssReg.Matches中的cssMatch(匹配.Groups[2].Value))
{
foreach(toSet中的HtmlNode thisNode)
{
AddStyle(thisNode,cssMatch.Groups[1].Value.Trim(),cssMatch.Groups[2].Value.Trim());
}
}
}
}
返回doc.DocumentNode.OuterHtml;
}
私有静态列表FindNodes(HtmlNode topNode,字符串[]toParse,int index)
{
可数myList;
字符串选择器=toParse[索引];
字符串elementName=“”;
字符串valueName=“”;
int valueType=0;
int idx=selector.IndexOfAny(新[]{'.','#'});
如果(idx>-1)
{
elementName=selector.Substring(0,idx);
valueName=选择器.子字符串(idx+1);
valueType=选择器。子字符串(idx,1)=“?”1:2;
}
其他的
{
elementName=选择器;
}
开关(valueType)
{
案例0:
myList=topNode.subjects(),其中(x=>x.Name==elementName);
打破
案例1:
myList=topNode.Subjections()。其中(x=>(elementName==“”| | x.Name==elementName)和&x.Attributes.Contains(“类”)和&x.Attributes[“类”]。Value.Split().Contains(valueName));
打破
案例2:
myList=topNode.subjects()。其中(x=>(elementName==“”| | x.Name==elementName)&&x.Id==valueName);
打破
违约:
抛出新的NotImplementedException();
}
如果(索引==toParse.Length-1)
{
返回新列表(myList);
}
List toReturn=新列表();
foreach(myList中的HtmlNode阳极)
{
toReturn.AddRange(FindNodes(阳极、托帕斯、索引+1));
}
回归回归;
}
私有静态void AddStyle(HtmlNode dest、string styleName、string styleValue)
{
如果(!dest.Attributes.Contains(“样式”))
{
dest.Attributes.Add(“style”,styleName+:“+styleValue);
}
其他的
{
HtmlAttribute attr=dest.Attributes[“style”];
如果(!attr.Value.Split().包含(styleName))
{
属性值=属性值+“”+styleName+”:“+styleValue;
}
}
}
问题不仅仅在于类(有些客户机实际上支持类),除非您的报表严格使用表构建,否则呈现出来时可能会很糟糕。目前beta版中有一个简洁的工具,它将采用一个“普通”的HTML页面,该页面使用的是“等”。。并将其转换为嵌套表:。它还将针对一些更常见的电子邮件客户端怪癖进行修复。是的,Outlook支持类,但不支持同一元素上的多个类。如果CSS只是简单地烘焙到HTML元素中,为什么看起来会有所不同?无论如何,报告主要是带有偶尔标题的表格。