ColorTranslator.FromHtml()到底支持什么,为什么它偏离了CSS颜色解释规则

本文关键字:CSS 规则 解释 颜色 为什么 FromHtml 什么 支持 ColorTranslator | 更新日期: 2023-09-27 17:59:31

我想在API中添加一个选项,让用户指定某种东西的颜色。我希望它能接受合理的颜色表示,可能包括十六进制表示和各种CSS颜色。乍一看,ColorTranslator.FromHtml()似乎是我需要的,但经过一点实验,我不太确定。

MSDN文档读起来不小心,似乎建议ColorTranslator.FromHtml()应该支持CSS颜色规范。然而,它似乎并不支持CSS提供的每一种语法。例如,这将引发一个异常:

// "rgb(255 is not a valid value for Int32"
Color c = ColorTranslator.FromHtml("rgb(255,255,255)");

其他颜色的解析与这里的CSS规则不同。8位十六进制代码被解释为AARGGBB,而不是RRGGBBAA:

Color c = ColorTranslator.FromHtml("#aabbccdd");
// 187, 204, 221, 170
Console.WriteLine($"{c.R}, {c.G}, {c.B}, {c.A}");

4位十六进制代码被解释为RRGG(蓝色部分为零),而不是RGBA:

Color c = ColorTranslator.FromHtml("#abcd");
// 0, 171, 205, 0
Console.WriteLine($"{c.R}, {c.G}, {c.B}, {c.A}");

为什么会存在这些偏差,该方法到底支持什么?只是因为该方法是旧的,冻结在旧版本的CSS中,但添加了一些与最近的CSS标准冲突的扩展吗?

ColorTranslator.FromHtml()到底支持什么,为什么它偏离了CSS颜色解释规则

我不能告诉你为什么,但我可以告诉你支持什么。

如果你阅读来源,你可以看到颜色是如何处理的,本质上是:

  • #771122作为#RRGGBB处理
  • #712作为#RGB处理
  • LightGrey转换为LightGray
  • 诸如highlighttext之类的任何系统颜色名称都被转换为特定的颜色
  • 任何其他颜色名称都将使用Color.FromName创建(我认为)

我想,这是在你所指的大多数东西都存在于规范中之前的一段时间。

如果将来有人需要这个。手动将HTML/CSS颜色解析为Color?(可为null的颜色)可能需要翻译成C#。可能需要添加对更多格式的支持,但我认为这涵盖了99%网络样式的典型基本情况。

    ''' <summary>
    ''' Named groups "r", "g", "b".
    ''' </summary>
    Private Shared Regex_Html_RRGGBB As New Regex("^#(?<r>[0-9a-f][0-9a-f])(?<g>[0-9a-f][0-9a-f])(?<b>[0-9a-f][0-9a-f])$", RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase Or RegexOptions.Compiled)
    ''' <summary>
    ''' Named groups "r", "g", "b".
    ''' </summary>
    Private Shared Regex_Html_RGB As New Regex("^#(?<r>[0-9a-f])(?<g>[0-9a-f])(?<b>[0-9a-f])$", RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase Or RegexOptions.Compiled)
    ''' <summary>
    ''' Named groups "r", "g", "b".
    ''' </summary>
    Private Shared Regex_Html_RGB_funct As New Regex("^rgb'((?<r>[0-255]{1,3})['t ]*,['t ]*(?<g>[0-255]{1,3})['t ]*,['t ]*(?<b>[0-255]{1,3})')$", RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase Or RegexOptions.Compiled)
    ''' <summary>
    ''' Named groups "r", "g", "b", "a" where A is only 0-1 range decimal.
    ''' </summary>
    Private Shared Regex_Html_RGBA_funct As New Regex("^rgba'((?<r>[0-255]{1,3})['t ]*,['t ]*(?<g>[0-255]{1,3})['t ]*,['t ]*(?<b>[0-255]{1,3}),['t ]*(?<a>[0-1]['.]{0,1}[0-9]{0,9})')$", RegexOptions.ExplicitCapture Or RegexOptions.IgnoreCase Or RegexOptions.Compiled)
    ''' <summary>
    ''' Reverse convertion from css format color to drawing color.
    ''' Supported formats #RRGGBB, #RGB, rgb(i,i,i), rgba(i,i,i,d), Named Colors.
    ''' Returns Nothing on failure.
    ''' </summary>
    ''' <param name="value"></param>
    Public Shared Function FromHtmlColor(value As String) As Color?
        If value.IsNullOrWhitespace Then
            Return Nothing
        Else ' must parse
            ' color translator doesn't account for transparancy.
            ' must check manually first.
            Dim m As Match
            m = Regex_Html_RGBA_funct.Match(value)
            If m.Success Then
                Dim r As Integer = ToRange(0, ToInt(m.Groups("r").Value), 255)
                Dim g As Integer = ToRange(0, ToInt(m.Groups("g").Value), 255)
                Dim b As Integer = ToRange(0, ToInt(m.Groups("b").Value), 255)
                Dim a As Integer = ToRange(0, ToInt(255 * ToDbl(m.Groups("a").Value)), 255) ' convert 0-1 to 0-255.
                Return Color.FromArgb(a, r, g, b)
            End If
            m = Regex_Html_RRGGBB.Match(value)
            If m.Success Then
                Dim r As Integer = Convert.ToInt32(m.Groups("r").Value, 16)
                Dim g As Integer = Convert.ToInt32(m.Groups("g").Value, 16)
                Dim b As Integer = Convert.ToInt32(m.Groups("b").Value, 16)
                Return Color.FromArgb(r, g, b)
            End If
            m = Regex_Html_RGB_funct.Match(value)
            If m.Success Then
                Dim r As Integer = ToRange(0, ToInt(m.Groups("r").Value), 255)
                Dim g As Integer = ToRange(0, ToInt(m.Groups("g").Value), 255)
                Dim b As Integer = ToRange(0, ToInt(m.Groups("b").Value), 255)
                Return Color.FromArgb(r, g, b)
            End If
            m = Regex_Html_RGB.Match(value) ' #rgb that needs values doubled before conversion.
            If m.Success Then
                Dim r As Integer = Convert.ToInt32(m.Groups("r").Value & m.Groups("r").Value, 16) ' double up each character for "#rgb"
                Dim g As Integer = Convert.ToInt32(m.Groups("g").Value & m.Groups("g").Value, 16)
                Dim b As Integer = Convert.ToInt32(m.Groups("b").Value & m.Groups("b").Value, 16)
                Return Color.FromArgb(r, g, b)
            End If
            Try ' color translator as final fallback (this supports very little surprisingly
                Return Drawing.ColorTranslator.FromHtml(value)
            Catch ex As Exception
                ' ignore.
            End Try
            Return Nothing
        End If
    End Function

基于该方法被称为FromHtml()而不是FromCss()的事实,该方法"偏离"CSS标准的原因可能是因为它一开始甚至没有尝试实现CSS。它到底支持什么?同样,根据方法名称,该方法似乎是对HTML颜色的广告支持。

对于那些不熟悉这两种语言的人来说,HTML和CSS是两种不同的语言,它们对颜色值的表示法并不完全相同。它们唯一的共同点是许多预定义的颜色名称,以及十六进制表示法。事实上,任何给定的方法都能正确地将6位十六进制代码解释为RGB颜色,这并不意味着该方法与CSS有任何关系。同样,HTML接受十六进制代码作为颜色值这一事实并不意味着它与CSS兼容。

举个例子:HTML没有rgb()函数表示法。HTML没有8位十六进制代码(它们被简单地视为6位十六进制码,忽略任何尾随数字)。HTML没有4位十六进制代码(它们被简单地视为6位十六进制码,并填充前4位)。

在CSS中,8位和4位十六进制代码仅在CSS-color-4中有效,这是一个相对较新的标准,与您链接到的标准相同。另一方面,这种方法已经存在多年了。一些浏览器可以声称实现了以后标准化的功能(WebKit尤其臭名昭著,但IE5经常被认为普及了后来的box-sizing属性),但要说这种方法也能做到这一点,那就太夸张了。