C# 中内联 CSS

发布于 2024-09-18 01:57:52 字数 15400 浏览 7 评论 0原文

我需要从 c# 中的样式表内联 css。

就像这是如何运作的。

http://www.mailchimp.com/labs/inlinecss.php

CSS很简单,只有类,没有花哨的选择器。

我正在考虑使用正则表达式 (?(?[^{}]+){(?

有没有更简单的方法?或者已经用 c# 编写的东西?

更新 - 2010 年 9 月 16 日,

如果您的 html 也是有效的 xml,我已经能够想出一个简单的 CSS 内联器。它使用正则表达式来获取

请注意,CssToXpath 尚未完全实现,有些事情它还无法做到。

CssInliner.cs

using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;

namespace CssInliner
{
    public class CssInliner
    {
        private static Regex _matchStyles = new Regex("\\s*(?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})",
                                                RegexOptions.IgnoreCase
                                                | RegexOptions.CultureInvariant
                                                | RegexOptions.IgnorePatternWhitespace
                                                | RegexOptions.Compiled
                                            );

        public List<Match> Styles { get; private set; }
        public string InlinedXhtml { get; private set; }

        private XElement XhtmlDocument { get; set; }

        public CssInliner(string xhtml)
        {
            XhtmlDocument = ParseXhtml(xhtml);
            Styles = GetStyleMatches();

            foreach (var style in Styles)
            {
                if (!style.Success)
                    return;

                var cssSelector = style.Groups["selector"].Value.Trim();
                var xpathSelector = CssToXpath.Transform(cssSelector);
                var cssStyle = style.Groups["style"].Value.Trim();

                foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
                {
                    var inlineStyle = element.Attribute("style");

                    var newInlineStyle = cssStyle + ";";
                    if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
                    {
                        newInlineStyle += inlineStyle.Value;
                    }

                    element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
                }
            }

            XhtmlDocument.Descendants("style").Remove();
            InlinedXhtml = XhtmlDocument.ToString();
        }

        private List<Match> GetStyleMatches()
        {
            var styles = new List<Match>();

            var styleElements = XhtmlDocument.Descendants("style");
            foreach (var styleElement in styleElements)
            {
                var matches = _matchStyles.Matches(styleElement.Value);

                foreach (Match match in matches)
                {
                    styles.Add(match);
                }
            }

            return styles;
        }

        private static XElement ParseXhtml(string xhtml)
        {
            return XElement.Parse(xhtml);
        }
    }
}

CssToXpath.cs

using System.Text.RegularExpressions;

namespace CssInliner
{
    public static class CssToXpath
    {
        public static string Transform(string css)
        {
            #region Translation Rules
            // References:  http://ejohn.org/blog/xpath-css-selectors/
            //              http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js
            var regexReplaces = new[] {
                                          // add @ for attribs
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
                                              Replace = @"[@$1$2]"
                                          },
                                          //  multiple queries
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
                                              Replace = @"|"
                                          },
                                          // , + ~ >
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
                                              Replace = @"$1"
                                          },
                                          //* ~ + >
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::*[1]/self::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/$2"
                                          },
                                          // all unescaped stuff escaped
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
                                              Replace = @"[$1='$2']"
                                          },
                                          // all descendant or self to //
                                          new RegexReplace {
                                              Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"$1*$2$3"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
                                              Replace = @"$1//$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
                                              Replace = @"//"
                                          },
                                          // :first-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
                                              Replace = @"*[1]/self::$1"
                                          },
                                          // :last-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
                                              Replace = @"$1[not(following-sibling::*)]"
                                          },
                                          // :only-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
                                              Replace = @"*[last()=1]/self::$1"
                                          },
                                          // :empty
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
                                              Replace = @"$1[not(*) and not(normalize-space())]"
                                          },
                                          // |= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
                                          },
                                          // *= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(@$1,$2)]"
                                          },
                                          // ~= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
                                          },
                                          // ^= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[starts-with(@$1,$2)]"
                                          },
                                          // != attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[not(@$1) or @$1!=$2]"
                                          },
                                          // ids
                                          new RegexReplace {
                                              Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[@id='$1']"
                                          },
                                          // classes
                                          new RegexReplace {
                                              Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
                                          },
                                          // normalize multiple filters
                                          new RegexReplace {
                                              Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
                                              Replace = @" and ($1)"
                                          },

                                      };
            #endregion

            foreach (var regexReplace in regexReplaces)
            {
                css = regexReplace.Regex.Replace(css, regexReplace.Replace);
            }

            return "//" + css;
        }
    }

    struct RegexReplace
    {
        public Regex Regex;
        public string Replace;
    }
}

和一些测试

    [TestMethod]
    public void TestCssToXpathRules()
    {
        var translations = new Dictionary<string, string>
                               {
                                   { "*", "//*" }, 
                                   { "p", "//p" }, 
                                   { "p > *", "//p/*" }, 
                                   { "#foo", "//*[@id='foo']" }, 
                                   { "*[title]", "//*[@title]" }, 
                                   { ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" }, 
                                   { "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
                               };

        foreach (var translation in translations)
        {
            var expected = translation.Value;
            var result = CssInliner.CssToXpath.Transform(translation.Key);

            Assert.AreEqual(expected, result);
        }
    }

    [TestMethod]
    public void HtmlWithMultiLineClassStyleReturnsInline()
    {
        #region var html = ...
        var html = XElement.Parse(@"<html>
                                        <head>
                                            <title>Hello, World Page!</title>
                                            <style>
                                                .redClass { 
                                                    background: red; 
                                                    color: purple; 
                                                }
                                            </style>
                                        </head>
                                        <body>
                                            <div class=""redClass"">Hello, World!</div>
                                        </body>
                                    </html>").ToString();
        #endregion

        #region const string expected ...
        var expected = XElement.Parse(@"<html>
                                            <head>
                                                <title>Hello, World Page!</title>
                                            </head>
                                            <body>
                                                <div class=""redClass"" style=""background: red; color: purple;"">Hello, World!</div>
                                            </body>
                                        </html>").ToString();
        #endregion

        var result = new CssInliner.CssInliner(html);

        Assert.AreEqual(expected, result.InlinedXhtml);
    }

还有更多测试,但是,它们导入 html 文件作为输入和预期输出,我不会发布所有这些!

但我应该发布规范化扩展方法!

private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
    return NormalizeSpaceRegex.Replace(data, @" ");
}

public static string NormalizeCharacter(this string data, char character)
{
    var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
    return normalizeCharacterRegex.Replace(data, character.ToString());
}

I need to inline css from a stylesheet in c#.

Like how this works.

http://www.mailchimp.com/labs/inlinecss.php

The css is simple, just classes, no fancy selectors.

I was contemplating using a regex (?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})+ to strip the rules from the css, and then attempting to do simple string replaces where the classes are called, but some of the html elements already have a style tag, so I'd have to account for that as well.

Is there a simpler approach? Or something already written in c#?

UPDATE - Sep 16, 2010

I've been able to come up with a simple CSS inliner provided your html is also valid xml. It uses a regex to get all the styles in your <style /> element. Then converts the css selectors to xpath expressions, and adds the style inline to the matching elements, before any pre-existing inline style.

Note, that the CssToXpath is not fully implemented, there are some things it just can't do... yet.

CssInliner.cs

using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;

namespace CssInliner
{
    public class CssInliner
    {
        private static Regex _matchStyles = new Regex("\\s*(?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})",
                                                RegexOptions.IgnoreCase
                                                | RegexOptions.CultureInvariant
                                                | RegexOptions.IgnorePatternWhitespace
                                                | RegexOptions.Compiled
                                            );

        public List<Match> Styles { get; private set; }
        public string InlinedXhtml { get; private set; }

        private XElement XhtmlDocument { get; set; }

        public CssInliner(string xhtml)
        {
            XhtmlDocument = ParseXhtml(xhtml);
            Styles = GetStyleMatches();

            foreach (var style in Styles)
            {
                if (!style.Success)
                    return;

                var cssSelector = style.Groups["selector"].Value.Trim();
                var xpathSelector = CssToXpath.Transform(cssSelector);
                var cssStyle = style.Groups["style"].Value.Trim();

                foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
                {
                    var inlineStyle = element.Attribute("style");

                    var newInlineStyle = cssStyle + ";";
                    if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
                    {
                        newInlineStyle += inlineStyle.Value;
                    }

                    element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
                }
            }

            XhtmlDocument.Descendants("style").Remove();
            InlinedXhtml = XhtmlDocument.ToString();
        }

        private List<Match> GetStyleMatches()
        {
            var styles = new List<Match>();

            var styleElements = XhtmlDocument.Descendants("style");
            foreach (var styleElement in styleElements)
            {
                var matches = _matchStyles.Matches(styleElement.Value);

                foreach (Match match in matches)
                {
                    styles.Add(match);
                }
            }

            return styles;
        }

        private static XElement ParseXhtml(string xhtml)
        {
            return XElement.Parse(xhtml);
        }
    }
}

CssToXpath.cs

using System.Text.RegularExpressions;

namespace CssInliner
{
    public static class CssToXpath
    {
        public static string Transform(string css)
        {
            #region Translation Rules
            // References:  http://ejohn.org/blog/xpath-css-selectors/
            //              http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js
            var regexReplaces = new[] {
                                          // add @ for attribs
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
                                              Replace = @"[@$1$2]"
                                          },
                                          //  multiple queries
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
                                              Replace = @"|"
                                          },
                                          // , + ~ >
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
                                              Replace = @"$1"
                                          },
                                          //* ~ + >
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::*[1]/self::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/$2"
                                          },
                                          // all unescaped stuff escaped
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
                                              Replace = @"[$1='$2']"
                                          },
                                          // all descendant or self to //
                                          new RegexReplace {
                                              Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"$1*$2$3"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
                                              Replace = @"$1//$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
                                              Replace = @"//"
                                          },
                                          // :first-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
                                              Replace = @"*[1]/self::$1"
                                          },
                                          // :last-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
                                              Replace = @"$1[not(following-sibling::*)]"
                                          },
                                          // :only-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
                                              Replace = @"*[last()=1]/self::$1"
                                          },
                                          // :empty
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
                                              Replace = @"$1[not(*) and not(normalize-space())]"
                                          },
                                          // |= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
                                          },
                                          // *= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(@$1,$2)]"
                                          },
                                          // ~= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
                                          },
                                          // ^= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[starts-with(@$1,$2)]"
                                          },
                                          // != attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[not(@$1) or @$1!=$2]"
                                          },
                                          // ids
                                          new RegexReplace {
                                              Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[@id='$1']"
                                          },
                                          // classes
                                          new RegexReplace {
                                              Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
                                          },
                                          // normalize multiple filters
                                          new RegexReplace {
                                              Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
                                              Replace = @" and ($1)"
                                          },

                                      };
            #endregion

            foreach (var regexReplace in regexReplaces)
            {
                css = regexReplace.Regex.Replace(css, regexReplace.Replace);
            }

            return "//" + css;
        }
    }

    struct RegexReplace
    {
        public Regex Regex;
        public string Replace;
    }
}

And some tests

    [TestMethod]
    public void TestCssToXpathRules()
    {
        var translations = new Dictionary<string, string>
                               {
                                   { "*", "//*" }, 
                                   { "p", "//p" }, 
                                   { "p > *", "//p/*" }, 
                                   { "#foo", "//*[@id='foo']" }, 
                                   { "*[title]", "//*[@title]" }, 
                                   { ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" }, 
                                   { "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
                               };

        foreach (var translation in translations)
        {
            var expected = translation.Value;
            var result = CssInliner.CssToXpath.Transform(translation.Key);

            Assert.AreEqual(expected, result);
        }
    }

    [TestMethod]
    public void HtmlWithMultiLineClassStyleReturnsInline()
    {
        #region var html = ...
        var html = XElement.Parse(@"<html>
                                        <head>
                                            <title>Hello, World Page!</title>
                                            <style>
                                                .redClass { 
                                                    background: red; 
                                                    color: purple; 
                                                }
                                            </style>
                                        </head>
                                        <body>
                                            <div class=""redClass"">Hello, World!</div>
                                        </body>
                                    </html>").ToString();
        #endregion

        #region const string expected ...
        var expected = XElement.Parse(@"<html>
                                            <head>
                                                <title>Hello, World Page!</title>
                                            </head>
                                            <body>
                                                <div class=""redClass"" style=""background: red; color: purple;"">Hello, World!</div>
                                            </body>
                                        </html>").ToString();
        #endregion

        var result = new CssInliner.CssInliner(html);

        Assert.AreEqual(expected, result.InlinedXhtml);
    }

There are more tests, but, they import html files for the input and expected output and I'm not posting all that!

But I should post the Normalize extension methods!

private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
    return NormalizeSpaceRegex.Replace(data, @" ");
}

public static string NormalizeCharacter(this string data, char character)
{
    var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
    return normalizeCharacterRegex.Replace(data, character.ToString());
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

猫九 2024-09-25 01:57:52

我在 Github 上有一个使 CSS 内联的项目。它非常简单,并且支持移动样式。在我的博客上阅读更多内容:http://martinnormark.com/move-css-inline-premailer -net

I have a project on Github that makes CSS inline. It's very simple, and support mobile styles. Read more on my blog: http://martinnormark.com/move-css-inline-premailer-net

书间行客 2024-09-25 01:57:52

既然您当前的实现已经完成了 90%,为什么不使用现有框架而是用 HTML 解析器替换 XML 解析呢?其中比较流行的包之一是 HTML Agility Pack。它支持 XPath 查询,甚至具有类似于为 XML 提供的标准 .NET 接口的 LINQ 接口,因此它应该是一个相当简单的替代品。

Since you're already 90% of the way there with your current implementation, why don't you use your existing framework but replace the XML parsing with an HTML parser instead? One of the more popular ones out there is the HTML Agility Pack. It supports XPath queries and even has a LINQ interface similar to the standard .NET interface provided for XML so it should be a fairly straightforward replacement.

梦在深巷 2024-09-25 01:57:52

由于这个选项在其他答复中不是很清楚,我认为它值得一个简单的答案。

使用PreMailer.Net

您所要做的就是:

  1. 通过 nuget 安装 PreMailer.NET。
  2. 输入:

    var inlineStyles = PreMailer.Net.PreMailer.MoveCssInline(htmlSource, false);
    目的地= inlineStyles.Html;
    

您就完成了!

顺便说一句,您可能需要添加 using 指令来缩短该行。

当然,上面的链接中有更多使用信息。

As this option is not very clear in the other replies, I think it deserves a straightforward answer.

Use PreMailer.Net.

All you have to do is:

  1. Install PreMailer.NET via nuget.
  2. Type this:

    var inlineStyles = PreMailer.Net.PreMailer.MoveCssInline(htmlSource, false);
    destination = inlineStyles.Html;
    

And you are done!

BTW, you may want to add a using directive to shorten that line.

More usage info in the link above, of course.

╭⌒浅淡时光〆 2024-09-25 01:57:52

很好的问题。

我不知道是否有 .NET 解决方案,但我发现了一个名为 Premailer 的 Ruby 程序 声称内联 CSS。如果您想使用它,您有几个选择:

  1. 用 C#(或您熟悉的任何 .NET 语言)重写 Premailer
  2. 使用 IronRuby 在 .NET 中运行 Ruby

Excellent question.

I have no idea if there is a .NET solution, but I found a Ruby program called Premailer that claims to inline CSS. If you want to use it you have a couple options:

  1. Rewrite Premailer in C# (or any .NET language you are familiar with)
  2. Use IronRuby to run Ruby in .NET
红玫瑰 2024-09-25 01:57:52

我建议使用实际的 CSS 解析器而不是正则表达式。您不需要解析完整的语言,因为您主要对复制感兴趣,但无论如何,此类解析器都是可用的(对于 .NET 也是如此)。例如,看一下antlr的语法列表,特别是CSS 2.1 语法CSS3 语法。如果您不介意次优结果(其中内联样式可能包含重复定义),您可以删除这两种语法的大部分内容,但要做到这一点,您需要了解内部 CSS 逻辑一些能够解析速记属性。

然而,从长远来看,这肯定比一系列永无休止的临时正则表达式修复要少很多工作。

I'd recommend using an actual CSS parser rather than Regexes. You don't need to parse the full language since you're interested mostly in reproduction, but in any case such parsers are available (and for .NET too). For example, take a look at antlr's list of grammars, specifically a CSS 2.1 grammar or a CSS3 grammar. You can possibly strip large parts of both grammars if you don't mind sub-optimal results wherein inline styles may include duplicate definitions, but to do this well, you'll need some idea of internal CSS logic to be able to resolve shorthand attributes.

In the long run, however, this will certainly be a lot less work than a neverending series of adhoc regex fixes.

染墨丶若流云 2024-09-25 01:57:52

这是一个想法,为什么不拨打 http://www.mailchimp.com /labs/inlinecss.php 使用 C#。从使用 firebug 的分析来看,post 调用需要 2 个参数 htmlstrip ,它们接受值(开/关),结果位于名为 text 的参数中。

这是关于如何使用 c# 进行 post 调用的示例

Here is an idea, why dont you make a post call to http://www.mailchimp.com/labs/inlinecss.php using c#. from analysis using firebug it looks like the post call needs 2 params html and strip which takes values (on/off) the result is in a param called text.

here is a sample on how to make a post call using c#

灯角 2024-09-25 01:57:52

Chad,你一定要添加 CSS 内联吗?或者您可以通过在 中添加

(抱歉,忘记添加代码的引号)

Chad, do you necessarily have to add the CSS inline? Or could you maybe be better off by adding a <style> block to your <head>? This will in essence replace the need for a reference to a CSS file as well plus maintain the rule that the actual inline rules override the ones set in the header/referenced css file.

(sorry, forgot to add the quotes for code)

如此安好 2024-09-25 01:57:52

我会推荐这样的字典:

private Dictionary<string, Dictionary<string, string>> cssDictionary = new Dictionary<string, Dictionary<string, string>();

我会解析 css 来填充这个 cssDictionary。

(添加“style-type”、“style-property”、“value”。例如:

Dictionary<string,string> bodyStyleDictionary = new Dictionary<string, string();
    bodyStyleDictionary.Add("background", "#000000");
    cssDictionary.Add("body", bodyStyleDictionary);

之后我最好将 HTML 转换为 XmlDocument。

您可以通过其子级递归地运行文档节点,并查找其父级(这甚至可以让您能够使用选择器)。

您可以在每个元素上检查元素类型、id 和类,然后浏览 cssDictionary 以将该元素的任何样式添加到 style 属性中(当然,您可以这样做)。如果它们具有重叠的属性,则可能需要按出现的顺序放置它们(并在最后添加现有的内联样式)。

完成后,您将 xmlDocument 作为字符串发出并删除第一行() 这应该会为您留下一个带有内联 css 的有效 html 文档。

当然,它可能看起来有点像黑客,但最终我认为这是一个非常可靠的解决方案,可以确保稳定性并且完全满足您的需求。

I would recommend a dictonary like this:

private Dictionary<string, Dictionary<string, string>> cssDictionary = new Dictionary<string, Dictionary<string, string>();

I would parse the css to fill this cssDictionary.

(Adding 'style-type', 'style-property', 'value'. In example:

Dictionary<string,string> bodyStyleDictionary = new Dictionary<string, string();
    bodyStyleDictionary.Add("background", "#000000");
    cssDictionary.Add("body", bodyStyleDictionary);

After that I would preferably convert the HTML to an XmlDocument.

You can recursively run through the documents nodes by it's children and also look up it's parents (This would even enable you being able to use selectors).

On each element you check for the element type, the id and the class. You then browse through the cssDictionary to add any styles for this element to the style attribute (Granted, you might want to place them in order of occurrence if they have overlapping properties (And add the existing inline styles the last).

When you're done, you emit the xmlDocument as a string and remove the first line (<?xml version="1.0"?>) This should leave you with a valid html document with inline css.

Sure, it might half look like a hack, but in the end I think it's a pretty solid solution that ensures stability and quite does what you seem to be looking for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文