65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 CSS 解析器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (9投票s)

2012年2月25日

CPOL

2分钟阅读

viewsIcon

64334

downloadIcon

3250

一个简单的 CSS 解析器,用于与 iTextSharp 配合进行 HTML 到 PDF 的生成。

引言

层叠样式表 (CSS) 允许开发人员为 Web 创建出色的用户界面。它们易于构建、使用和维护。当使用其内置的 HTML 到 PDF 功能时,iTextSharp 可以利用 CSS。要将样式表信息从 CSS 导入到 iTextSharp 中,开发人员需要读取 CSS 文件并将其转换为 Dictionary,以便 iTextSharp 使用。本文将演示一个简单的解决方案,以执行该任务。包含的解决方案包括单元测试和一个 ASP.Net 项目,演示了如何使用 CSSParser

背景

在开发 HTML 到 PDF 实用程序时,我发现需要解析层叠样式表。互联网上有许多 CSS 解析器,但没有一个符合我的需求。我用 C# 创建了这个简单的基于正则表达式的 CSS 解析器,以促进在 iTextSharp 中生成 PDF。CSS 解析器的要求如下:

要求

  1. 读取 CSS 文件
  2. 将 CSS 存储在集合中
  3. 查询类及其属性
  4. 查询元素及其属性
  5. 易于维护和增强
  6. 轻松地将样式信息馈送到 iTextSharp 中,将 HTML 转换为 PDF
  7. 它应该精简
  8. 其他开发人员可以使用

使用代码

CSSParser 继承自一个泛型的 List ,其中包含 KeyValuePair。键将是 CSS 选择器。值将是另一组键值对。这里的键是 CSS 属性名称。值将是 CSS 属性值。我使用了一个泛型的 List 而不是 Dictionary,因为层叠样式表可以多次列出相同的选择器或属性。

public partial class CSSParser : List<KeyValuePair<String,List<KeyValuePair<String,String>>>>, ICSSParser

CSS 解析器的核心是一个正则表达式,我在 Stack Overflow 上找到 (http://stackoverflow.com/a/2694121/899290)。CSSGroups 正则表达式将获取样式表并将其分解为命名组。在解析 CSS 之前,将使用 CSSComments 正则表达式从文件中删除 CSS 注释。

public const String CSSGroups = @"(?<selector>(?:(?:[^,{]+),?)*?)\{(?:(?<name>[^}:]+):?(?<value>[^};]+);?)*?\}";

public const String CSSComments = @"(?<!"")\/\*.+?\*\/(?!"")";

private Regex rStyles = new Regex(CSSGroups, RegexOptions.IgnoreCase | RegexOptions.Compiled);

Read 方法负责解析样式表中的值并填充泛型 List。它将使用 .Net Regex 类来删除任何注释并填充集合。

public void Read(String CascadingStyleSheet)
{
    this.StyleSheet = CascadingStyleSheet;

    if (!String.IsNullOrEmpty(CascadingStyleSheet))
    {
        //Remove comments before parsing the CSS. Don't want any comments in the collection.
        MatchCollection MatchList = rStyles.Matches(Regex.Replace(CascadingStyleSheet, 
            RegularExpressionLibrary.CSSComments, String.Empty));
        foreach (Match item in MatchList)
        {
            //Check for nulls
            if (item != null && item.Groups != null && 
                item.Groups[SelectorKey] != null && 
                item.Groups[SelectorKey].Captures != null && 
                item.Groups[SelectorKey].Captures[0] != null && 
                !String.IsNullOrEmpty(item.Groups[SelectorKey].Value))
            {
                String strSelector = item.Groups[SelectorKey].Captures[0].Value.Trim();
                var style = new List<KeyValuePair<String,String>>();

                for (int i = 0; i < item.Groups[NameKey].Captures.Count; i++)
                {
                    String className = item.Groups[NameKey].Captures[i].Value;
                    String value = item.Groups[ValueKey].Captures[i].Value;
                    //Check for null values in the properies
                    if (!String.IsNullOrEmpty(className) && !String.IsNullOrEmpty(value))
                    {
                        className = className.TrimWhiteSpace();
                        value = value.TrimWhiteSpace();
                        //One more check to be sure we are only pulling valid css values
                        if (!String.IsNullOrEmpty(className) && !String.IsNullOrEmpty(value))
                        {
                            style.Add(new KeyValuePair<String,String>(className, value));
                        }
                    }
                }
                this.Add(new KeyValuePair<String,List<KeyValuePair<String,String>>>(strSelector, style));
            }
        }
    }
}

一旦列表填充完毕,使用 LINQ 或 Lambda 表达式来提取您需要的信息就变得很简单了。ClassesElements 属性将样式表的值公开为 Dictionary,可以将其提供给 iTextSharp

public Dictionary<String, Dictionary<String,String>> Classes
{
    get
    {
        if (classes == null || classes.Count == 0)
        {
            this.classes = this.Where(cl => cl.Key.StartsWith("."))
                .ToDictionary(cl => cl.Key.Trim(new Char[] { '.' }), cl => cl.Value
                    .ToDictionary(p => p.Key, p => p.Value));
        }

        return classes;
    }
}

public public Dictionary<String, Dictionary<String,String>> Elements
{
    get
    {
        if (elements == null || elements.Count == 0)
        {
            elements = this.Where(el => !el.Key.StartsWith("."))
                .ToDictionary(el => el.Key, el => el.Value
                    .ToDictionary(p => p.Key, p => p.Value));
        }
        return elements;
    }
}

使用 CSS 解析器

CSSParser 为您提供了两个读取层叠样式表的选项,读取 CSS 文件或字符串。ReadCSSFile 方法将读取 CSS 文件并填充集合。您可以通过调用 Read 方法或将 CSS 值传递给构造函数来读取包含 CSS 信息的字符串。

void lnkParseCSSFile_Click(object sender, EventArgs e)
{
    CSSParser parser = new CSSParser();
    parser.ReadCSSFile(Server.MapPath("~/CSSParserStyle.css"));
    //Display the Original CSS with some formating for the web
    this.divOriginalCSS.InnerHtml = parser.StyleSheet.FixLineBreakForWeb().FixTabsForWeb().FixSpaceForWeb();
    //Display the parsed CSS
    this.divParsedCSS.InnerHtml = parser.ToString();
    this.spnOriginalCSSLength.InnerText = parser.StyleSheet.Length.ToString();
    this.spnParsedCSSLength.InnerText = this.divParsedCSS.InnerHtml.Length.ToString();
}

关注点

CSSParserElementsClasses 属性针对 iTextSharp 版本 5.x

历史

  1. 版本 1.0 - 初始发布
© . All rights reserved.