# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" Color string parser for Pyface.
The ``parse_text`` function allows the creation of Color objects from
CSS-style strings, including all the CSS names colours or hex strings starting
with "#". If there is no match, a ColorParseError is raised.
A dictionary of named colours is available as the color_table module-level
dictionary. This dictionary holds all CSS colour names, plus a number of
other names such as "transparent".
Additionally, two utility functions ``channels_to_ints`` and
``ints_to_channels`` are provided to assist in converting between floating
point and integer values.
"""
import re
from .color_helpers import ints_to_channels
#: A dictionary mapping known color names to rgba tuples.
color_table = {
"aliceblue": (0.941, 0.973, 1.000, 1.0),
"antiquewhite": (0.980, 0.922, 0.843, 1.0),
"aqua": (0.000, 1.000, 1.000, 1.0),
"aquamarine": (0.498, 1.000, 0.831, 1.0),
"azure": (0.941, 1.000, 1.000, 1.0),
"beige": (0.961, 0.961, 0.863, 1.0),
"bisque": (1.000, 0.894, 0.769, 1.0),
"black": (0.000, 0.000, 0.000, 1.0),
"blanchedalmond": (1.000, 0.922, 0.804, 1.0),
"blue": (0.000, 0.000, 1.000, 1.0),
"blueviolet": (0.541, 0.169, 0.886, 1.0),
"brown": (0.647, 0.165, 0.165, 1.0),
"burlywood": (0.871, 0.722, 0.529, 1.0),
"cadetblue": (0.373, 0.620, 0.627, 1.0),
"chartreuse": (0.498, 1.000, 0.000, 1.0),
"chocolate": (0.824, 0.412, 0.118, 1.0),
"coral": (1.000, 0.498, 0.314, 1.0),
"cornflowerblue": (0.392, 0.584, 0.929, 1.0),
"cornsilk": (1.000, 0.973, 0.863, 1.0),
"crimson": (0.863, 0.078, 0.235, 1.0),
"cyan": (0.000, 1.000, 1.000, 1.0),
"darkblue": (0.000, 0.000, 0.545, 1.0),
"darkcyan": (0.000, 0.545, 0.545, 1.0),
"darkgoldenrod": (0.722, 0.525, 0.043, 1.0),
"darkgray": (0.663, 0.663, 0.663, 1.0),
"darkgreen": (0.000, 0.392, 0.000, 1.0),
"darkgrey": (0.663, 0.663, 0.663, 1.0),
"darkkhaki": (0.741, 0.718, 0.420, 1.0),
"darkmagenta": (0.545, 0.000, 0.545, 1.0),
"darkolivegreen": (0.333, 0.420, 0.184, 1.0),
"darkorange": (1.000, 0.549, 0.000, 1.0),
"darkorchid": (0.600, 0.196, 0.800, 1.0),
"darkred": (0.545, 0.000, 0.000, 1.0),
"darksalmon": (0.914, 0.588, 0.478, 1.0),
"darkseagreen": (0.561, 0.737, 0.561, 1.0),
"darkslateblue": (0.282, 0.239, 0.545, 1.0),
"darkslategray": (0.184, 0.310, 0.310, 1.0),
"darkslategrey": (0.184, 0.310, 0.310, 1.0),
"darkturquoise": (0.000, 0.808, 0.820, 1.0),
"darkviolet": (0.580, 0.000, 0.827, 1.0),
"deeppink": (1.000, 0.078, 0.576, 1.0),
"deepskyblue": (0.000, 0.749, 1.000, 1.0),
"dimgray": (0.412, 0.412, 0.412, 1.0),
"dimgrey": (0.412, 0.412, 0.412, 1.0),
"dodgerblue": (0.118, 0.565, 1.000, 1.0),
"firebrick": (0.698, 0.133, 0.133, 1.0),
"floralwhite": (1.000, 0.980, 0.941, 1.0),
"forestgreen": (0.133, 0.545, 0.133, 1.0),
"fuchsia": (1.000, 0.000, 1.000, 1.0),
"gainsboro": (0.863, 0.863, 0.863, 1.0),
"ghostwhite": (0.973, 0.973, 1.000, 1.0),
"gold": (1.000, 0.843, 0.000, 1.0),
"goldenrod": (0.855, 0.647, 0.125, 1.0),
"gray": (0.502, 0.502, 0.502, 1.0),
"green": (0.000, 0.502, 0.000, 1.0),
"greenyellow": (0.678, 1.000, 0.184, 1.0),
"grey": (0.502, 0.502, 0.502, 1.0),
"honeydew": (0.941, 1.000, 0.941, 1.0),
"hotpink": (1.000, 0.412, 0.706, 1.0),
"indianred": (0.804, 0.361, 0.361, 1.0),
"indigo": (0.294, 0.000, 0.510, 1.0),
"ivory": (1.000, 1.000, 0.941, 1.0),
"khaki": (0.941, 0.902, 0.549, 1.0),
"lavender": (0.902, 0.902, 0.980, 1.0),
"lavenderblush": (1.000, 0.941, 0.961, 1.0),
"lawngreen": (0.486, 0.988, 0.000, 1.0),
"lemonchiffon": (1.000, 0.980, 0.804, 1.0),
"lightblue": (0.678, 0.847, 0.902, 1.0),
"lightcoral": (0.941, 0.502, 0.502, 1.0),
"lightcyan": (0.878, 1.000, 1.000, 1.0),
"lightgoldenrodyellow": (0.980, 0.980, 0.824, 1.0),
"lightgray": (0.827, 0.827, 0.827, 1.0),
"lightgreen": (0.565, 0.933, 0.565, 1.0),
"lightgrey": (0.827, 0.827, 0.827, 1.0),
"lightpink": (1.000, 0.714, 0.757, 1.0),
"lightsalmon": (1.000, 0.627, 0.478, 1.0),
"lightseagreen": (0.125, 0.698, 0.667, 1.0),
"lightskyblue": (0.529, 0.808, 0.980, 1.0),
"lightslategray": (0.467, 0.533, 0.600, 1.0),
"lightslategrey": (0.467, 0.533, 0.600, 1.0),
"lightsteelblue": (0.690, 0.769, 0.871, 1.0),
"lightyellow": (1.000, 1.000, 0.878, 1.0),
"lime": (0.000, 1.000, 0.000, 1.0),
"limegreen": (0.196, 0.804, 0.196, 1.0),
"linen": (0.980, 0.941, 0.902, 1.0),
"magenta": (1.000, 0.000, 1.000, 1.0),
"maroon": (0.502, 0.000, 0.000, 1.0),
"mediumaquamarine": (0.400, 0.804, 0.667, 1.0),
"mediumblue": (0.000, 0.000, 0.804, 1.0),
"mediumorchid": (0.729, 0.333, 0.827, 1.0),
"mediumpurple": (0.576, 0.439, 0.859, 1.0),
"mediumseagreen": (0.235, 0.702, 0.443, 1.0),
"mediumslateblue": (0.482, 0.408, 0.933, 1.0),
"mediumspringgreen": (0.000, 0.980, 0.604, 1.0),
"mediumturquoise": (0.282, 0.820, 0.800, 1.0),
"mediumvioletred": (0.780, 0.082, 0.522, 1.0),
"midnightblue": (0.098, 0.098, 0.439, 1.0),
"mintcream": (0.961, 1.000, 0.980, 1.0),
"mistyrose": (1.000, 0.894, 0.882, 1.0),
"moccasin": (1.000, 0.894, 0.710, 1.0),
"navajowhite": (1.000, 0.871, 0.678, 1.0),
"navy": (0.000, 0.000, 0.502, 1.0),
"oldlace": (0.992, 0.961, 0.902, 1.0),
"olive": (0.502, 0.502, 0.000, 1.0),
"olivedrab": (0.420, 0.557, 0.137, 1.0),
"orange": (1.000, 0.647, 0.000, 1.0),
"orangered": (1.000, 0.271, 0.000, 1.0),
"orchid": (0.855, 0.439, 0.839, 1.0),
"palegoldenrod": (0.933, 0.910, 0.667, 1.0),
"palegreen": (0.596, 0.984, 0.596, 1.0),
"paleturquoise": (0.686, 0.933, 0.933, 1.0),
"palevioletred": (0.859, 0.439, 0.576, 1.0),
"papayawhip": (1.000, 0.937, 0.835, 1.0),
"peachpuff": (1.000, 0.855, 0.725, 1.0),
"peru": (0.804, 0.522, 0.247, 1.0),
"pink": (1.000, 0.753, 0.796, 1.0),
"plum": (0.867, 0.627, 0.867, 1.0),
"powderblue": (0.690, 0.878, 0.902, 1.0),
"purple": (0.502, 0.000, 0.502, 1.0),
"red": (1.000, 0.000, 0.000, 1.0),
"rosybrown": (0.737, 0.561, 0.561, 1.0),
"royalblue": (0.255, 0.412, 0.882, 1.0),
"saddlebrown": (0.545, 0.271, 0.075, 1.0),
"salmon": (0.980, 0.502, 0.447, 1.0),
"sandybrown": (0.957, 0.643, 0.376, 1.0),
"seagreen": (0.180, 0.545, 0.341, 1.0),
"seashell": (1.000, 0.961, 0.933, 1.0),
"sienna": (0.627, 0.322, 0.176, 1.0),
"silver": (0.753, 0.753, 0.753, 1.0),
"skyblue": (0.529, 0.808, 0.922, 1.0),
"slateblue": (0.416, 0.353, 0.804, 1.0),
"slategray": (0.439, 0.502, 0.565, 1.0),
"slategrey": (0.439, 0.502, 0.565, 1.0),
"snow": (1.000, 0.980, 0.980, 1.0),
"springgreen": (0.000, 1.000, 0.498, 1.0),
"steelblue": (0.275, 0.510, 0.706, 1.0),
"tan": (0.824, 0.706, 0.549, 1.0),
"teal": (0.000, 0.502, 0.502, 1.0),
"thistle": (0.847, 0.749, 0.847, 1.0),
"tomato": (1.000, 0.388, 0.278, 1.0),
"turquoise": (0.251, 0.878, 0.816, 1.0),
"violet": (0.933, 0.510, 0.933, 1.0),
"wheat": (0.961, 0.871, 0.702, 1.0),
"white": (1.000, 1.000, 1.000, 1.0),
"whitesmoke": (0.961, 0.961, 0.961, 1.0),
"yellow": (1.000, 1.000, 0.000, 1.0),
"yellowgreen": (0.604, 0.804, 0.196, 1.0),
"rebeccapurple": (0.4, 0.2, 0.6, 1.0),
# Several aliases for transparent
"clear": (0.0, 0.0, 0.0, 0.0),
"transparent": (0.0, 0.0, 0.0, 0.0),
"none": (0.0, 0.0, 0.0, 0.0),
}
# Translation table for stripping extraneous characters out of names.
ignored = str.maketrans({' ': None, '-': None, '_': None})
def _parse_name(text):
""" Parse a color name.
Parameters
----------
text : str
A string holding a color name, including all CSS color names, plus
any additional names found in pyface.color.color_table. The names
are normalized to lower case and stripped of whitespace, hyphens and
underscores.
Returns
-------
result : (space, channels), or None
Either a tuple of the form ('rgba', channels), where channels is a
tuple of 4 floating point values between 0.0 and 1.0, inclusive;
or None if there is no matching color name.
"""
text = text.lower()
text = text.translate(ignored)
if text in color_table:
return 'rgba', color_table[text]
return None
def _parse_hex(text):
""" Parse a hex form of a color.
Parameters
----------
text : str
A string holding a hex representation of the color in the form
'#RGB', '#RGBA', '#RRGGBB', '#RRGGBBAA', '#RRRRGGGGBBBB', or
'#RRRRGGGGBBBBAAAA'.
Returns
-------
result : (space, channels), or None
Either a tuple of the form (space, channels), where space is one of
'rgb' or 'rgba' and channels is a tuple of 3 or 4 floating point
values between 0.0 and 1.0, inclusive; or None if no hex
representation could be matched.
"""
text = text.strip()
if re.match("#[0-9a-fA-F]+", text) is None:
return None
text = text[1:]
if len(text) in {3, 4}:
step = 1
elif len(text) in {6, 8}:
step = 2
elif len(text) in {12, 16}:
step = 4
else:
return None
maximum = (1 << 4 * step) - 1
channels = ints_to_channels(
(int(text[i:i+step], 16) for i in range(0, len(text), step)),
maximum=maximum,
)
space = 'rgb' if len(channels) == 3 else 'rgba'
return space, channels
[docs]class ColorParseError(ValueError):
""" An Exception raised when parsing fails. """
pass
[docs]def parse_text(text):
""" Parse a text representation of a color.
Parameters
----------
text : str
A string holding the representation of the color. This can be:
- a color name, including all CSS color names, plus any additional
names found in pyface.color.color_table. The names are normalized
to lower case and stripped of whitespace, hyphens and underscores.
- a hex representation of the color in the form '#RGB', '#RGBA',
'#RRGGBB', '#RRGGBBAA', '#RRRRGGGGBBBB', or '#RRRRGGGGBBBBAAAA'.
Returns
-------
space : str
A string describing the color space for the channels. Will be one of
'rgb' or 'rgba'.
channels : tuple of floats
The channel values as a tuple of 3 or 4 floating point values between
0.0 and 1.0, inclusive.
Raises
------
ColorParseError
If the string cannot be converted to a valid color.
"""
result = None
for parser in _parse_hex, _parse_name:
result = parser(text)
if result is not None:
return result
else:
raise ColorParseError(
'Unable to parse color value in string {!r}'.format(text)
)