diff --git a/src/backend/color.py b/src/backend/color.py index a66c089c..0d903b71 100644 --- a/src/backend/color.py +++ b/src/backend/color.py @@ -7,14 +7,11 @@ import builtins import colorsys from copy import copy from dataclasses import InitVar, dataclass, field +from enum import Enum from typing import Optional, Tuple, Union from hsluv import hex_to_hsluv, hsluv_to_hex, hsluv_to_rgb, rgb_to_hsluv -from .svg_colors import SVG_COLORS - -HEX_TO_SVG = {v: k for k, v in SVG_COLORS.items()} - ColorTuple = Tuple[float, float, float, float] @@ -23,12 +20,10 @@ class Color: """A color manipulable in HSLuv, HSL, RGB, hexadecimal and by SVG name. The `Color` object constructor accepts hexadecimal string - ("#RGB", "#RRGGBB" or "#RRGGBBAA"), - [CSS/SVG named colors](https://www.december.com/html/spec/colorsvg.html), - or another `Color` to copy. + ("#RGB", "#RRGGBB" or "#RRGGBBAA") or another `Color` to copy. - Attributes representing the color in HSLuv, HSL, RGB and hexadecimal - formats can be accessed and modified on these `Color` objects. + Attributes representing the color in HSLuv, HSL, RGB, hexadecimal and + SVG name formats can be accessed and modified on these `Color` objects. The `hsluv()`/`hsluva()`, `hsl()`/`hsla()` and `rgb()`/`rgba()` functions in this module are provided to create an object by specifying @@ -46,20 +41,18 @@ class Color: # capping the value between 0-100, as hsluv handles numbers outside # this range incorrectly. - color_hex_or_name: InitVar[str] = "black" - hue: float = field(init=False, default=0) - _saturation: float = field(init=False, default=0) - _luv: float = field(init=False, default=0) - alpha: float = field(init=False, default=1) + color_or_hex: InitVar[str] = "#00000000" + hue: float = field(init=False, default=0) + _saturation: float = field(init=False, default=0) + _luv: float = field(init=False, default=0) + alpha: float = field(init=False, default=1) - def __post_init__(self, color_hex_or_name: Union["Color", str]) -> None: - if isinstance(color_hex_or_name, Color): - hsluva = color_hex_or_name.hsluva + def __post_init__(self, color_or_hex: Union["Color", str]) -> None: + if isinstance(color_or_hex, Color): + hsluva = color_or_hex.hsluva self.hue, self.saturation, self.luv, self.alpha = hsluva - elif color_hex_or_name.startswith("#"): - self.hex = color_hex_or_name else: - self.name = color_hex_or_name + self.hex = color_or_hex # HSLuv @@ -151,7 +144,8 @@ class Color: def hex(self) -> str: rgb = hsluv_to_hex(self.hsluva) alpha = builtins.hex(int(self.alpha * 255))[2:] - return f"{rgb}{alpha if self.alpha < 1 else ''}".lower() + alpha = f"0{alpha}" if len(alpha) == 1 else alpha + return f"{alpha if self.alpha < 1 else ''}{rgb}".lower() @hex.setter def hex(self, value: str) -> None: @@ -167,11 +161,14 @@ class Color: @property def name(self) -> Optional[str]: - return HEX_TO_SVG.get(self.hex) + try: + return SVGColor(self.hex).name + except ValueError: + return None @name.setter def name(self, value: str) -> None: - self.hex = SVG_COLORS[value.lower()] + self.hex = SVGColor[value.lower()].value.hex # Other methods @@ -283,6 +280,160 @@ class Color: return new +class SVGColor(Enum): + """Standard SVG/HTML/CSS colors, with the addition of `transparent`.""" + + aliceblue = Color("#f0f8ff") + antiquewhite = Color("#faebd7") + aqua = Color("#00ffff") + aquamarine = Color("#7fffd4") + azure = Color("#f0ffff") + beige = Color("#f5f5dc") + bisque = Color("#ffe4c4") + black = Color("#000000") + blanchedalmond = Color("#ffebcd") + blue = Color("#0000ff") + blueviolet = Color("#8a2be2") + brown = Color("#a52a2a") + burlywood = Color("#deb887") + cadetblue = Color("#5f9ea0") + chartreuse = Color("#7fff00") + chocolate = Color("#d2691e") + coral = Color("#ff7f50") + cornflowerblue = Color("#6495ed") + cornsilk = Color("#fff8dc") + crimson = Color("#dc143c") + cyan = Color("#00ffff") + darkblue = Color("#00008b") + darkcyan = Color("#008b8b") + darkgoldenrod = Color("#b8860b") + darkgray = Color("#a9a9a9") + darkgreen = Color("#006400") + darkgrey = Color("#a9a9a9") + darkkhaki = Color("#bdb76b") + darkmagenta = Color("#8b008b") + darkolivegreen = Color("#556b2f") + darkorange = Color("#ff8c00") + darkorchid = Color("#9932cc") + darkred = Color("#8b0000") + darksalmon = Color("#e9967a") + darkseagreen = Color("#8fbc8f") + darkslateblue = Color("#483d8b") + darkslategray = Color("#2f4f4f") + darkslategrey = Color("#2f4f4f") + darkturquoise = Color("#00ced1") + darkviolet = Color("#9400d3") + deeppink = Color("#ff1493") + deepskyblue = Color("#00bfff") + dimgray = Color("#696969") + dimgrey = Color("#696969") + dodgerblue = Color("#1e90ff") + firebrick = Color("#b22222") + floralwhite = Color("#fffaf0") + forestgreen = Color("#228b22") + fuchsia = Color("#ff00ff") + gainsboro = Color("#dcdcdc") + ghostwhite = Color("#f8f8ff") + gold = Color("#ffd700") + goldenrod = Color("#daa520") + gray = Color("#808080") + green = Color("#008000") + greenyellow = Color("#adff2f") + grey = Color("#808080") + honeydew = Color("#f0fff0") + hotpink = Color("#ff69b4") + indianred = Color("#cd5c5c") + indigo = Color("#4b0082") + ivory = Color("#fffff0") + khaki = Color("#f0e68c") + lavender = Color("#e6e6fa") + lavenderblush = Color("#fff0f5") + lawngreen = Color("#7cfc00") + lemonchiffon = Color("#fffacd") + lightblue = Color("#add8e6") + lightcoral = Color("#f08080") + lightcyan = Color("#e0ffff") + lightgoldenrodyellow = Color("#fafad2") + lightgray = Color("#d3d3d3") + lightgreen = Color("#90ee90") + lightgrey = Color("#d3d3d3") + lightpink = Color("#ffb6c1") + lightsalmon = Color("#ffa07a") + lightseagreen = Color("#20b2aa") + lightskyblue = Color("#87cefa") + lightslategray = Color("#778899") + lightslategrey = Color("#778899") + lightsteelblue = Color("#b0c4de") + lightyellow = Color("#ffffe0") + lime = Color("#00ff00") + limegreen = Color("#32cd32") + linen = Color("#faf0e6") + magenta = Color("#ff00ff") + maroon = Color("#800000") + mediumaquamarine = Color("#66cdaa") + mediumblue = Color("#0000cd") + mediumorchid = Color("#ba55d3") + mediumpurple = Color("#9370db") + mediumseagreen = Color("#3cb371") + mediumslateblue = Color("#7b68ee") + mediumspringgreen = Color("#00fa9a") + mediumturquoise = Color("#48d1cc") + mediumvioletred = Color("#c71585") + midnightblue = Color("#191970") + mintcream = Color("#f5fffa") + mistyrose = Color("#ffe4e1") + moccasin = Color("#ffe4b5") + navajowhite = Color("#ffdead") + navy = Color("#000080") + oldlace = Color("#fdf5e6") + olive = Color("#808000") + olivedrab = Color("#6b8e23") + orange = Color("#ffa500") + orangered = Color("#ff4500") + orchid = Color("#da70d6") + palegoldenrod = Color("#eee8aa") + palegreen = Color("#98fb98") + paleturquoise = Color("#afeeee") + palevioletred = Color("#db7093") + papayawhip = Color("#ffefd5") + peachpuff = Color("#ffdab9") + peru = Color("#cd853f") + pink = Color("#ffc0cb") + plum = Color("#dda0dd") + powderblue = Color("#b0e0e6") + purple = Color("#800080") + rebeccapurple = Color("#663399") + red = Color("#ff0000") + rosybrown = Color("#bc8f8f") + royalblue = Color("#4169e1") + saddlebrown = Color("#8b4513") + salmon = Color("#fa8072") + sandybrown = Color("#f4a460") + seagreen = Color("#2e8b57") + seashell = Color("#fff5ee") + sienna = Color("#a0522d") + silver = Color("#c0c0c0") + skyblue = Color("#87ceeb") + slateblue = Color("#6a5acd") + slategray = Color("#708090") + slategrey = Color("#708090") + snow = Color("#fffafa") + springgreen = Color("#00ff7f") + steelblue = Color("#4682b4") + tan = Color("#d2b48c") + teal = Color("#008080") + thistle = Color("#d8bfd8") + tomato = Color("#ff6347") + transparent = Color("#00000000") # not standard but exists in QML + turquoise = Color("#40e0d0") + violet = Color("#ee82ee") + wheat = Color("#f5deb3") + white = Color("#ffffff") + whitesmoke = Color("#f5f5f5") + yellow = Color("#ffff00") + yellowgreen = Color("#9acd32") + + def hsluva( hue: float = 0, saturation: float = 0, luv: float = 0, alpha: float = 1, ) -> Color: @@ -304,6 +455,7 @@ def rgba( return Color().but(red=red, green=green, blue=blue, alpha=alpha) +# Aliases color = Color hsluv = hsluva hsl = hsla diff --git a/src/backend/html_markdown.py b/src/backend/html_markdown.py index 108bb098..aa0c1321 100644 --- a/src/backend/html_markdown.py +++ b/src/backend/html_markdown.py @@ -4,6 +4,7 @@ """HTML and Markdown processing tools.""" import re +from contextlib import suppress from typing import Dict, List, Optional, Tuple from urllib.parse import unquote @@ -14,7 +15,7 @@ import nio from html_sanitizer.sanitizer import Sanitizer from lxml.html import HtmlElement, etree # nosec -from .svg_colors import SVG_COLORS +from .color import SVGColor class MarkdownInlineGrammar(mistune.InlineGrammar): @@ -74,11 +75,12 @@ class MarkdownInlineLexer(mistune.InlineLexer): class MarkdownRenderer(mistune.Renderer): - def color(self, color: str, text: str): + def color(self, color: str, text: str) -> str: """Render given text with a color using ``.""" - # This may be a color name, try to get a #hex code for it. - color = SVG_COLORS.get(re.sub(r"\s", "", color.lower()), color) + # This may be a SVG color name, try to get a #hex code from it: + with suppress(KeyError): + color = SVGColor[color.lower().replace(" ", "")].value.hex return f'{text}' diff --git a/src/backend/svg_colors.py b/src/backend/svg_colors.py deleted file mode 100644 index 870d2a3d..00000000 --- a/src/backend/svg_colors.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright 2018 Dave Eddy - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Standard SVG/CSS colors - https://github.com/bahamas10/css-color-names""" - -SVG_COLORS = { - "aliceblue": "#f0f8ff", - "antiquewhite": "#faebd7", - "aqua": "#00ffff", - "aquamarine": "#7fffd4", - "azure": "#f0ffff", - "beige": "#f5f5dc", - "bisque": "#ffe4c4", - "black": "#000000", - "blanchedalmond": "#ffebcd", - "blue": "#0000ff", - "blueviolet": "#8a2be2", - "brown": "#a52a2a", - "burlywood": "#deb887", - "cadetblue": "#5f9ea0", - "chartreuse": "#7fff00", - "chocolate": "#d2691e", - "coral": "#ff7f50", - "cornflowerblue": "#6495ed", - "cornsilk": "#fff8dc", - "crimson": "#dc143c", - "cyan": "#00ffff", - "darkblue": "#00008b", - "darkcyan": "#008b8b", - "darkgoldenrod": "#b8860b", - "darkgray": "#a9a9a9", - "darkgreen": "#006400", - "darkgrey": "#a9a9a9", - "darkkhaki": "#bdb76b", - "darkmagenta": "#8b008b", - "darkolivegreen": "#556b2f", - "darkorange": "#ff8c00", - "darkorchid": "#9932cc", - "darkred": "#8b0000", - "darksalmon": "#e9967a", - "darkseagreen": "#8fbc8f", - "darkslateblue": "#483d8b", - "darkslategray": "#2f4f4f", - "darkslategrey": "#2f4f4f", - "darkturquoise": "#00ced1", - "darkviolet": "#9400d3", - "deeppink": "#ff1493", - "deepskyblue": "#00bfff", - "dimgray": "#696969", - "dimgrey": "#696969", - "dodgerblue": "#1e90ff", - "firebrick": "#b22222", - "floralwhite": "#fffaf0", - "forestgreen": "#228b22", - "fuchsia": "#ff00ff", - "gainsboro": "#dcdcdc", - "ghostwhite": "#f8f8ff", - "goldenrod": "#daa520", - "gold": "#ffd700", - "gray": "#808080", - "green": "#008000", - "greenyellow": "#adff2f", - "grey": "#808080", - "honeydew": "#f0fff0", - "hotpink": "#ff69b4", - "indianred": "#cd5c5c", - "indigo": "#4b0082", - "ivory": "#fffff0", - "khaki": "#f0e68c", - "lavenderblush": "#fff0f5", - "lavender": "#e6e6fa", - "lawngreen": "#7cfc00", - "lemonchiffon": "#fffacd", - "lightblue": "#add8e6", - "lightcoral": "#f08080", - "lightcyan": "#e0ffff", - "lightgoldenrodyellow": "#fafad2", - "lightgray": "#d3d3d3", - "lightgreen": "#90ee90", - "lightgrey": "#d3d3d3", - "lightpink": "#ffb6c1", - "lightsalmon": "#ffa07a", - "lightseagreen": "#20b2aa", - "lightskyblue": "#87cefa", - "lightslategray": "#778899", - "lightslategrey": "#778899", - "lightsteelblue": "#b0c4de", - "lightyellow": "#ffffe0", - "lime": "#00ff00", - "limegreen": "#32cd32", - "linen": "#faf0e6", - "magenta": "#ff00ff", - "maroon": "#800000", - "mediumaquamarine": "#66cdaa", - "mediumblue": "#0000cd", - "mediumorchid": "#ba55d3", - "mediumpurple": "#9370db", - "mediumseagreen": "#3cb371", - "mediumslateblue": "#7b68ee", - "mediumspringgreen": "#00fa9a", - "mediumturquoise": "#48d1cc", - "mediumvioletred": "#c71585", - "midnightblue": "#191970", - "mintcream": "#f5fffa", - "mistyrose": "#ffe4e1", - "moccasin": "#ffe4b5", - "navajowhite": "#ffdead", - "navy": "#000080", - "oldlace": "#fdf5e6", - "olive": "#808000", - "olivedrab": "#6b8e23", - "orange": "#ffa500", - "orangered": "#ff4500", - "orchid": "#da70d6", - "palegoldenrod": "#eee8aa", - "palegreen": "#98fb98", - "paleturquoise": "#afeeee", - "palevioletred": "#db7093", - "papayawhip": "#ffefd5", - "peachpuff": "#ffdab9", - "peru": "#cd853f", - "pink": "#ffc0cb", - "plum": "#dda0dd", - "powderblue": "#b0e0e6", - "purple": "#800080", - "rebeccapurple": "#663399", - "red": "#ff0000", - "rosybrown": "#bc8f8f", - "royalblue": "#4169e1", - "saddlebrown": "#8b4513", - "salmon": "#fa8072", - "sandybrown": "#f4a460", - "seagreen": "#2e8b57", - "seashell": "#fff5ee", - "sienna": "#a0522d", - "silver": "#c0c0c0", - "skyblue": "#87ceeb", - "slateblue": "#6a5acd", - "slategray": "#708090", - "slategrey": "#708090", - "snow": "#fffafa", - "springgreen": "#00ff7f", - "steelblue": "#4682b4", - "tan": "#d2b48c", - "teal": "#008080", - "thistle": "#d8bfd8", - "tomato": "#ff6347", - "turquoise": "#40e0d0", - "violet": "#ee82ee", - "wheat": "#f5deb3", - "white": "#ffffff", - "whitesmoke": "#f5f5f5", - "yellow": "#ffff00", - "yellowgreen": "#9acd32", -}