使用C#w/MVC将CSS展平为HTML

使用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", "

我有一个小问题,从HTML创建电子邮件,并让它们在Outlook中正确显示

现在我们都知道Outlook的HTML呈现有点糟糕。(礼貌地说!)基本上最好的方法是将样式烘焙到HTML元素中。然而,我想把现有的基于网络的报告,并通过电子邮件发送出去。显然,报告使用CSS类的样式完成得很好

所以我需要做的是解析HTML页面和CSS,并构建一个扁平的HTML,我可以将其发送出去

使用JavaScript和jQuery这将是相对简单的,类似于

$("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元素中,为什么看起来会有所不同?无论如何,报告主要是带有偶尔标题的表格。