color.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. """Color definitions are used as per the CSS3
  2. [CSS Color Module Level 3](http://www.w3.org/TR/css3-color/#svg-color) specification.
  3. A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`.
  4. In these cases the _last_ color when sorted alphabetically takes preferences,
  5. eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua".
  6. Warning: Deprecated
  7. The `Color` class is deprecated, use `pydantic_extra_types` instead.
  8. See [`pydantic-extra-types.Color`](../usage/types/extra_types/color_types.md)
  9. for more information.
  10. """
  11. import math
  12. import re
  13. from colorsys import hls_to_rgb, rgb_to_hls
  14. from typing import Any, Callable, Optional, Union, cast
  15. from pydantic_core import CoreSchema, PydanticCustomError, core_schema
  16. from typing_extensions import deprecated
  17. from ._internal import _repr
  18. from ._internal._schema_generation_shared import GetJsonSchemaHandler as _GetJsonSchemaHandler
  19. from .json_schema import JsonSchemaValue
  20. from .warnings import PydanticDeprecatedSince20
  21. ColorTuple = Union[tuple[int, int, int], tuple[int, int, int, float]]
  22. ColorType = Union[ColorTuple, str]
  23. HslColorTuple = Union[tuple[float, float, float], tuple[float, float, float, float]]
  24. class RGBA:
  25. """Internal use only as a representation of a color."""
  26. __slots__ = 'r', 'g', 'b', 'alpha', '_tuple'
  27. def __init__(self, r: float, g: float, b: float, alpha: Optional[float]):
  28. self.r = r
  29. self.g = g
  30. self.b = b
  31. self.alpha = alpha
  32. self._tuple: tuple[float, float, float, Optional[float]] = (r, g, b, alpha)
  33. def __getitem__(self, item: Any) -> Any:
  34. return self._tuple[item]
  35. # these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached
  36. _r_255 = r'(\d{1,3}(?:\.\d+)?)'
  37. _r_comma = r'\s*,\s*'
  38. _r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)'
  39. _r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?'
  40. _r_sl = r'(\d{1,3}(?:\.\d+)?)%'
  41. r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*'
  42. r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*'
  43. # CSS3 RGB examples: rgb(0, 0, 0), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 50%)
  44. r_rgb = rf'\s*rgba?\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
  45. # CSS3 HSL examples: hsl(270, 60%, 50%), hsla(270, 60%, 50%, 0.5), hsla(270, 60%, 50%, 50%)
  46. r_hsl = rf'\s*hsla?\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
  47. # CSS4 RGB examples: rgb(0 0 0), rgb(0 0 0 / 0.5), rgb(0 0 0 / 50%), rgba(0 0 0 / 50%)
  48. r_rgb_v4_style = rf'\s*rgba?\(\s*{_r_255}\s+{_r_255}\s+{_r_255}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
  49. # CSS4 HSL examples: hsl(270 60% 50%), hsl(270 60% 50% / 0.5), hsl(270 60% 50% / 50%), hsla(270 60% 50% / 50%)
  50. r_hsl_v4_style = rf'\s*hsla?\(\s*{_r_h}\s+{_r_sl}\s+{_r_sl}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
  51. # colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used
  52. repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'}
  53. rads = 2 * math.pi
  54. @deprecated(
  55. 'The `Color` class is deprecated, use `pydantic_extra_types` instead. '
  56. 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/.',
  57. category=PydanticDeprecatedSince20,
  58. )
  59. class Color(_repr.Representation):
  60. """Represents a color."""
  61. __slots__ = '_original', '_rgba'
  62. def __init__(self, value: ColorType) -> None:
  63. self._rgba: RGBA
  64. self._original: ColorType
  65. if isinstance(value, (tuple, list)):
  66. self._rgba = parse_tuple(value)
  67. elif isinstance(value, str):
  68. self._rgba = parse_str(value)
  69. elif isinstance(value, Color):
  70. self._rgba = value._rgba
  71. value = value._original
  72. else:
  73. raise PydanticCustomError(
  74. 'color_error', 'value is not a valid color: value must be a tuple, list or string'
  75. )
  76. # if we've got here value must be a valid color
  77. self._original = value
  78. @classmethod
  79. def __get_pydantic_json_schema__(
  80. cls, core_schema: core_schema.CoreSchema, handler: _GetJsonSchemaHandler
  81. ) -> JsonSchemaValue:
  82. field_schema = {}
  83. field_schema.update(type='string', format='color')
  84. return field_schema
  85. def original(self) -> ColorType:
  86. """Original value passed to `Color`."""
  87. return self._original
  88. def as_named(self, *, fallback: bool = False) -> str:
  89. """Returns the name of the color if it can be found in `COLORS_BY_VALUE` dictionary,
  90. otherwise returns the hexadecimal representation of the color or raises `ValueError`.
  91. Args:
  92. fallback: If True, falls back to returning the hexadecimal representation of
  93. the color instead of raising a ValueError when no named color is found.
  94. Returns:
  95. The name of the color, or the hexadecimal representation of the color.
  96. Raises:
  97. ValueError: When no named color is found and fallback is `False`.
  98. """
  99. if self._rgba.alpha is None:
  100. rgb = cast(tuple[int, int, int], self.as_rgb_tuple())
  101. try:
  102. return COLORS_BY_VALUE[rgb]
  103. except KeyError as e:
  104. if fallback:
  105. return self.as_hex()
  106. else:
  107. raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e
  108. else:
  109. return self.as_hex()
  110. def as_hex(self) -> str:
  111. """Returns the hexadecimal representation of the color.
  112. Hex string representing the color can be 3, 4, 6, or 8 characters depending on whether the string
  113. a "short" representation of the color is possible and whether there's an alpha channel.
  114. Returns:
  115. The hexadecimal representation of the color.
  116. """
  117. values = [float_to_255(c) for c in self._rgba[:3]]
  118. if self._rgba.alpha is not None:
  119. values.append(float_to_255(self._rgba.alpha))
  120. as_hex = ''.join(f'{v:02x}' for v in values)
  121. if all(c in repeat_colors for c in values):
  122. as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2))
  123. return '#' + as_hex
  124. def as_rgb(self) -> str:
  125. """Color as an `rgb(<r>, <g>, <b>)` or `rgba(<r>, <g>, <b>, <a>)` string."""
  126. if self._rgba.alpha is None:
  127. return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})'
  128. else:
  129. return (
  130. f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, '
  131. f'{round(self._alpha_float(), 2)})'
  132. )
  133. def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple:
  134. """Returns the color as an RGB or RGBA tuple.
  135. Args:
  136. alpha: Whether to include the alpha channel. There are three options for this input:
  137. - `None` (default): Include alpha only if it's set. (e.g. not `None`)
  138. - `True`: Always include alpha.
  139. - `False`: Always omit alpha.
  140. Returns:
  141. A tuple that contains the values of the red, green, and blue channels in the range 0 to 255.
  142. If alpha is included, it is in the range 0 to 1.
  143. """
  144. r, g, b = (float_to_255(c) for c in self._rgba[:3])
  145. if alpha is None:
  146. if self._rgba.alpha is None:
  147. return r, g, b
  148. else:
  149. return r, g, b, self._alpha_float()
  150. elif alpha:
  151. return r, g, b, self._alpha_float()
  152. else:
  153. # alpha is False
  154. return r, g, b
  155. def as_hsl(self) -> str:
  156. """Color as an `hsl(<h>, <s>, <l>)` or `hsl(<h>, <s>, <l>, <a>)` string."""
  157. if self._rgba.alpha is None:
  158. h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore
  159. return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})'
  160. else:
  161. h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore
  162. return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})'
  163. def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple:
  164. """Returns the color as an HSL or HSLA tuple.
  165. Args:
  166. alpha: Whether to include the alpha channel.
  167. - `None` (default): Include the alpha channel only if it's set (e.g. not `None`).
  168. - `True`: Always include alpha.
  169. - `False`: Always omit alpha.
  170. Returns:
  171. The color as a tuple of hue, saturation, lightness, and alpha (if included).
  172. All elements are in the range 0 to 1.
  173. Note:
  174. This is HSL as used in HTML and most other places, not HLS as used in Python's `colorsys`.
  175. """
  176. h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) # noqa: E741
  177. if alpha is None:
  178. if self._rgba.alpha is None:
  179. return h, s, l
  180. else:
  181. return h, s, l, self._alpha_float()
  182. if alpha:
  183. return h, s, l, self._alpha_float()
  184. else:
  185. # alpha is False
  186. return h, s, l
  187. def _alpha_float(self) -> float:
  188. return 1 if self._rgba.alpha is None else self._rgba.alpha
  189. @classmethod
  190. def __get_pydantic_core_schema__(
  191. cls, source: type[Any], handler: Callable[[Any], CoreSchema]
  192. ) -> core_schema.CoreSchema:
  193. return core_schema.with_info_plain_validator_function(
  194. cls._validate, serialization=core_schema.to_string_ser_schema()
  195. )
  196. @classmethod
  197. def _validate(cls, __input_value: Any, _: Any) -> 'Color':
  198. return cls(__input_value)
  199. def __str__(self) -> str:
  200. return self.as_named(fallback=True)
  201. def __repr_args__(self) -> '_repr.ReprArgs':
  202. return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())]
  203. def __eq__(self, other: Any) -> bool:
  204. return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple()
  205. def __hash__(self) -> int:
  206. return hash(self.as_rgb_tuple())
  207. def parse_tuple(value: tuple[Any, ...]) -> RGBA:
  208. """Parse a tuple or list to get RGBA values.
  209. Args:
  210. value: A tuple or list.
  211. Returns:
  212. An `RGBA` tuple parsed from the input tuple.
  213. Raises:
  214. PydanticCustomError: If tuple is not valid.
  215. """
  216. if len(value) == 3:
  217. r, g, b = (parse_color_value(v) for v in value)
  218. return RGBA(r, g, b, None)
  219. elif len(value) == 4:
  220. r, g, b = (parse_color_value(v) for v in value[:3])
  221. return RGBA(r, g, b, parse_float_alpha(value[3]))
  222. else:
  223. raise PydanticCustomError('color_error', 'value is not a valid color: tuples must have length 3 or 4')
  224. def parse_str(value: str) -> RGBA:
  225. """Parse a string representing a color to an RGBA tuple.
  226. Possible formats for the input string include:
  227. * named color, see `COLORS_BY_NAME`
  228. * hex short eg. `<prefix>fff` (prefix can be `#`, `0x` or nothing)
  229. * hex long eg. `<prefix>ffffff` (prefix can be `#`, `0x` or nothing)
  230. * `rgb(<r>, <g>, <b>)`
  231. * `rgba(<r>, <g>, <b>, <a>)`
  232. Args:
  233. value: A string representing a color.
  234. Returns:
  235. An `RGBA` tuple parsed from the input string.
  236. Raises:
  237. ValueError: If the input string cannot be parsed to an RGBA tuple.
  238. """
  239. value_lower = value.lower()
  240. try:
  241. r, g, b = COLORS_BY_NAME[value_lower]
  242. except KeyError:
  243. pass
  244. else:
  245. return ints_to_rgba(r, g, b, None)
  246. m = re.fullmatch(r_hex_short, value_lower)
  247. if m:
  248. *rgb, a = m.groups()
  249. r, g, b = (int(v * 2, 16) for v in rgb)
  250. if a:
  251. alpha: Optional[float] = int(a * 2, 16) / 255
  252. else:
  253. alpha = None
  254. return ints_to_rgba(r, g, b, alpha)
  255. m = re.fullmatch(r_hex_long, value_lower)
  256. if m:
  257. *rgb, a = m.groups()
  258. r, g, b = (int(v, 16) for v in rgb)
  259. if a:
  260. alpha = int(a, 16) / 255
  261. else:
  262. alpha = None
  263. return ints_to_rgba(r, g, b, alpha)
  264. m = re.fullmatch(r_rgb, value_lower) or re.fullmatch(r_rgb_v4_style, value_lower)
  265. if m:
  266. return ints_to_rgba(*m.groups()) # type: ignore
  267. m = re.fullmatch(r_hsl, value_lower) or re.fullmatch(r_hsl_v4_style, value_lower)
  268. if m:
  269. return parse_hsl(*m.groups()) # type: ignore
  270. raise PydanticCustomError('color_error', 'value is not a valid color: string not recognised as a valid color')
  271. def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float] = None) -> RGBA:
  272. """Converts integer or string values for RGB color and an optional alpha value to an `RGBA` object.
  273. Args:
  274. r: An integer or string representing the red color value.
  275. g: An integer or string representing the green color value.
  276. b: An integer or string representing the blue color value.
  277. alpha: A float representing the alpha value. Defaults to None.
  278. Returns:
  279. An instance of the `RGBA` class with the corresponding color and alpha values.
  280. """
  281. return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha))
  282. def parse_color_value(value: Union[int, str], max_val: int = 255) -> float:
  283. """Parse the color value provided and return a number between 0 and 1.
  284. Args:
  285. value: An integer or string color value.
  286. max_val: Maximum range value. Defaults to 255.
  287. Raises:
  288. PydanticCustomError: If the value is not a valid color.
  289. Returns:
  290. A number between 0 and 1.
  291. """
  292. try:
  293. color = float(value)
  294. except ValueError:
  295. raise PydanticCustomError('color_error', 'value is not a valid color: color values must be a valid number')
  296. if 0 <= color <= max_val:
  297. return color / max_val
  298. else:
  299. raise PydanticCustomError(
  300. 'color_error',
  301. 'value is not a valid color: color values must be in the range 0 to {max_val}',
  302. {'max_val': max_val},
  303. )
  304. def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]:
  305. """Parse an alpha value checking it's a valid float in the range 0 to 1.
  306. Args:
  307. value: The input value to parse.
  308. Returns:
  309. The parsed value as a float, or `None` if the value was None or equal 1.
  310. Raises:
  311. PydanticCustomError: If the input value cannot be successfully parsed as a float in the expected range.
  312. """
  313. if value is None:
  314. return None
  315. try:
  316. if isinstance(value, str) and value.endswith('%'):
  317. alpha = float(value[:-1]) / 100
  318. else:
  319. alpha = float(value)
  320. except ValueError:
  321. raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be a valid float')
  322. if math.isclose(alpha, 1):
  323. return None
  324. elif 0 <= alpha <= 1:
  325. return alpha
  326. else:
  327. raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be in the range 0 to 1')
  328. def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA:
  329. """Parse raw hue, saturation, lightness, and alpha values and convert to RGBA.
  330. Args:
  331. h: The hue value.
  332. h_units: The unit for hue value.
  333. sat: The saturation value.
  334. light: The lightness value.
  335. alpha: Alpha value.
  336. Returns:
  337. An instance of `RGBA`.
  338. """
  339. s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100)
  340. h_value = float(h)
  341. if h_units in {None, 'deg'}:
  342. h_value = h_value % 360 / 360
  343. elif h_units == 'rad':
  344. h_value = h_value % rads / rads
  345. else:
  346. # turns
  347. h_value = h_value % 1
  348. r, g, b = hls_to_rgb(h_value, l_value, s_value)
  349. return RGBA(r, g, b, parse_float_alpha(alpha))
  350. def float_to_255(c: float) -> int:
  351. """Converts a float value between 0 and 1 (inclusive) to an integer between 0 and 255 (inclusive).
  352. Args:
  353. c: The float value to be converted. Must be between 0 and 1 (inclusive).
  354. Returns:
  355. The integer equivalent of the given float value rounded to the nearest whole number.
  356. Raises:
  357. ValueError: If the given float value is outside the acceptable range of 0 to 1 (inclusive).
  358. """
  359. return int(round(c * 255))
  360. COLORS_BY_NAME = {
  361. 'aliceblue': (240, 248, 255),
  362. 'antiquewhite': (250, 235, 215),
  363. 'aqua': (0, 255, 255),
  364. 'aquamarine': (127, 255, 212),
  365. 'azure': (240, 255, 255),
  366. 'beige': (245, 245, 220),
  367. 'bisque': (255, 228, 196),
  368. 'black': (0, 0, 0),
  369. 'blanchedalmond': (255, 235, 205),
  370. 'blue': (0, 0, 255),
  371. 'blueviolet': (138, 43, 226),
  372. 'brown': (165, 42, 42),
  373. 'burlywood': (222, 184, 135),
  374. 'cadetblue': (95, 158, 160),
  375. 'chartreuse': (127, 255, 0),
  376. 'chocolate': (210, 105, 30),
  377. 'coral': (255, 127, 80),
  378. 'cornflowerblue': (100, 149, 237),
  379. 'cornsilk': (255, 248, 220),
  380. 'crimson': (220, 20, 60),
  381. 'cyan': (0, 255, 255),
  382. 'darkblue': (0, 0, 139),
  383. 'darkcyan': (0, 139, 139),
  384. 'darkgoldenrod': (184, 134, 11),
  385. 'darkgray': (169, 169, 169),
  386. 'darkgreen': (0, 100, 0),
  387. 'darkgrey': (169, 169, 169),
  388. 'darkkhaki': (189, 183, 107),
  389. 'darkmagenta': (139, 0, 139),
  390. 'darkolivegreen': (85, 107, 47),
  391. 'darkorange': (255, 140, 0),
  392. 'darkorchid': (153, 50, 204),
  393. 'darkred': (139, 0, 0),
  394. 'darksalmon': (233, 150, 122),
  395. 'darkseagreen': (143, 188, 143),
  396. 'darkslateblue': (72, 61, 139),
  397. 'darkslategray': (47, 79, 79),
  398. 'darkslategrey': (47, 79, 79),
  399. 'darkturquoise': (0, 206, 209),
  400. 'darkviolet': (148, 0, 211),
  401. 'deeppink': (255, 20, 147),
  402. 'deepskyblue': (0, 191, 255),
  403. 'dimgray': (105, 105, 105),
  404. 'dimgrey': (105, 105, 105),
  405. 'dodgerblue': (30, 144, 255),
  406. 'firebrick': (178, 34, 34),
  407. 'floralwhite': (255, 250, 240),
  408. 'forestgreen': (34, 139, 34),
  409. 'fuchsia': (255, 0, 255),
  410. 'gainsboro': (220, 220, 220),
  411. 'ghostwhite': (248, 248, 255),
  412. 'gold': (255, 215, 0),
  413. 'goldenrod': (218, 165, 32),
  414. 'gray': (128, 128, 128),
  415. 'green': (0, 128, 0),
  416. 'greenyellow': (173, 255, 47),
  417. 'grey': (128, 128, 128),
  418. 'honeydew': (240, 255, 240),
  419. 'hotpink': (255, 105, 180),
  420. 'indianred': (205, 92, 92),
  421. 'indigo': (75, 0, 130),
  422. 'ivory': (255, 255, 240),
  423. 'khaki': (240, 230, 140),
  424. 'lavender': (230, 230, 250),
  425. 'lavenderblush': (255, 240, 245),
  426. 'lawngreen': (124, 252, 0),
  427. 'lemonchiffon': (255, 250, 205),
  428. 'lightblue': (173, 216, 230),
  429. 'lightcoral': (240, 128, 128),
  430. 'lightcyan': (224, 255, 255),
  431. 'lightgoldenrodyellow': (250, 250, 210),
  432. 'lightgray': (211, 211, 211),
  433. 'lightgreen': (144, 238, 144),
  434. 'lightgrey': (211, 211, 211),
  435. 'lightpink': (255, 182, 193),
  436. 'lightsalmon': (255, 160, 122),
  437. 'lightseagreen': (32, 178, 170),
  438. 'lightskyblue': (135, 206, 250),
  439. 'lightslategray': (119, 136, 153),
  440. 'lightslategrey': (119, 136, 153),
  441. 'lightsteelblue': (176, 196, 222),
  442. 'lightyellow': (255, 255, 224),
  443. 'lime': (0, 255, 0),
  444. 'limegreen': (50, 205, 50),
  445. 'linen': (250, 240, 230),
  446. 'magenta': (255, 0, 255),
  447. 'maroon': (128, 0, 0),
  448. 'mediumaquamarine': (102, 205, 170),
  449. 'mediumblue': (0, 0, 205),
  450. 'mediumorchid': (186, 85, 211),
  451. 'mediumpurple': (147, 112, 219),
  452. 'mediumseagreen': (60, 179, 113),
  453. 'mediumslateblue': (123, 104, 238),
  454. 'mediumspringgreen': (0, 250, 154),
  455. 'mediumturquoise': (72, 209, 204),
  456. 'mediumvioletred': (199, 21, 133),
  457. 'midnightblue': (25, 25, 112),
  458. 'mintcream': (245, 255, 250),
  459. 'mistyrose': (255, 228, 225),
  460. 'moccasin': (255, 228, 181),
  461. 'navajowhite': (255, 222, 173),
  462. 'navy': (0, 0, 128),
  463. 'oldlace': (253, 245, 230),
  464. 'olive': (128, 128, 0),
  465. 'olivedrab': (107, 142, 35),
  466. 'orange': (255, 165, 0),
  467. 'orangered': (255, 69, 0),
  468. 'orchid': (218, 112, 214),
  469. 'palegoldenrod': (238, 232, 170),
  470. 'palegreen': (152, 251, 152),
  471. 'paleturquoise': (175, 238, 238),
  472. 'palevioletred': (219, 112, 147),
  473. 'papayawhip': (255, 239, 213),
  474. 'peachpuff': (255, 218, 185),
  475. 'peru': (205, 133, 63),
  476. 'pink': (255, 192, 203),
  477. 'plum': (221, 160, 221),
  478. 'powderblue': (176, 224, 230),
  479. 'purple': (128, 0, 128),
  480. 'red': (255, 0, 0),
  481. 'rosybrown': (188, 143, 143),
  482. 'royalblue': (65, 105, 225),
  483. 'saddlebrown': (139, 69, 19),
  484. 'salmon': (250, 128, 114),
  485. 'sandybrown': (244, 164, 96),
  486. 'seagreen': (46, 139, 87),
  487. 'seashell': (255, 245, 238),
  488. 'sienna': (160, 82, 45),
  489. 'silver': (192, 192, 192),
  490. 'skyblue': (135, 206, 235),
  491. 'slateblue': (106, 90, 205),
  492. 'slategray': (112, 128, 144),
  493. 'slategrey': (112, 128, 144),
  494. 'snow': (255, 250, 250),
  495. 'springgreen': (0, 255, 127),
  496. 'steelblue': (70, 130, 180),
  497. 'tan': (210, 180, 140),
  498. 'teal': (0, 128, 128),
  499. 'thistle': (216, 191, 216),
  500. 'tomato': (255, 99, 71),
  501. 'turquoise': (64, 224, 208),
  502. 'violet': (238, 130, 238),
  503. 'wheat': (245, 222, 179),
  504. 'white': (255, 255, 255),
  505. 'whitesmoke': (245, 245, 245),
  506. 'yellow': (255, 255, 0),
  507. 'yellowgreen': (154, 205, 50),
  508. }
  509. COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()}