initial commit

This commit is contained in:
2026-01-17 21:51:34 -05:00
commit 06a449b44e
90 changed files with 24094 additions and 0 deletions

11
deps/inkex/colors/spaces/__init__.py vendored Normal file
View File

@@ -0,0 +1,11 @@
"""
Each color space that this module supports such have one file in this module.
"""
from .cmyk import ColorDeviceCMYK
from .cms import ColorCMS
from .hsl import ColorHSL
from .hsv import ColorHSV
from .named import ColorNamed
from .none import ColorNone
from .rgb import ColorRGB

95
deps/inkex/colors/spaces/cms.py vendored Normal file
View File

@@ -0,0 +1,95 @@
# coding=utf-8
#
# Copyright (C) 2024 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
"""
SVG icc-color parser
"""
from ..color import Color, AlphaNotAllowed, ColorError, round_by_type
from .css import CssColor
from .rgb import ColorRGB
class ColorCMS(CssColor, AlphaNotAllowed):
"""
Parse and print SVG icc-color objects into their values and the fallback RGB
"""
name = "cms"
css_func = "icc-color"
channels = ()
scales = ()
def __init__(self, values, icc_profile=None, fallback=None):
if isinstance(values, str):
if values.strip().startswith("#") and " " in values:
fallback, values = values.split(" ", 1)
fallback = Color(fallback)
icc_profile, values = self.parse_css_color(values)
if icc_profile is None:
raise ColorError("CMS Color requires an icc color profile name.")
self.icc_profile = icc_profile
self.fallback_rgb = fallback
super().__init__(values)
def __str__(self) -> str:
values = self.css_join.join([f"{v:g}" for v in self.get_css_values()])
fallback = str(ColorRGB(self.fallback_rgb)) + " " if self.fallback_rgb else ""
return f"{fallback}{self.css_func}({self.icc_profile}, {values})"
@classmethod
def can_parse(cls, string: str) -> bool:
# Custom detection because of RGB fallback prefix
return "icc-color" in string.replace("(", " ").split()
@classmethod
def constrain(cls, index, value):
return min(max(round_by_type(float, value), 0.0), 1.0)
@classmethod
def scale_up(cls, index, value):
return value # All cms values are already 0.0 to 1.0
@classmethod
def scale_down(cls, index, value):
return value # All cms values are already 0.0 to 1.0
@staticmethod
def convert_to_rgb(*data):
"""Catch attempted conversions to rgb"""
raise NotImplementedError("Can not convert to RGB from icc color")
@staticmethod
def convert_from_rgb(*data):
"""Catch attempted conversions from rgb"""
raise NotImplementedError("Can not convert from RGB to icc color")
# This is research code for a future developer to use. We already use PIL and this will
# allow icc colors to be converted in python. This isn't needed right now, so this work
# will be left undone.
# @staticmethod
# def convert_to_rgb():
# from PIL import Image, ImageCms
# pixel = Image.fromarray([[int(r * 255), int(g * 255), int(b * 255)]], 'RGB')
# transform = ImageCms.buildTransform(sRGB_profile, self.this_profile, "RGB",
# self.this_profile_mode, self.this_rendering_intent, 0)
# transform.apply_in_place(pixel)
# return [p / 255 for p in pixel[0]]

81
deps/inkex/colors/spaces/cmyk.py vendored Normal file
View File

@@ -0,0 +1,81 @@
# coding=utf-8
#
# Copyright (C) 2024 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# pylint: disable=W0223
"""
DeviceCMYK Color Space
"""
from .css import CssColorModule4
class ColorDeviceCMYK(CssColorModule4):
"""
Parse the device-cmyk CSS Color Module 4 format.
Note that this format is NOT true CMYK as you might expect in a printer and
is instead is an aproximation of the intended ink levels if this was converted
into a real CMYK color profile using a color management system.
"""
name = "cmyk"
channels = ("cyan", "magenta", "yellow", "black")
scales = ((0, 100), (0, 100), (0, 100), (0, 100), (0.0, 1.0))
css_either_prefix = "device-cmyk"
cyan = property(
lambda self: self[0], lambda self, value: self.__setitem__(0, value)
)
magenta = property(
lambda self: self[1], lambda self, value: self.__setitem__(1, value)
)
yellow = property(
lambda self: self[2], lambda self, value: self.__setitem__(2, value)
)
black = property(
lambda self: self[3], lambda self, value: self.__setitem__(3, value)
)
@staticmethod
def convert_to_rgb(cyan, magenta, yellow, black, *alpha):
"""
Convert a set of Device-CMYK identities into RGB
"""
white = 1.0 - black
return [
1.0 - min((1.0, cyan * white + black)),
1.0 - min((1.0, magenta * white + black)),
1.0 - min((1.0, yellow * white + black)),
] + list(alpha)
@staticmethod
def convert_from_rgb(red, green, blue, *alpha):
"""
Convert RGB into Device-CMYK
"""
white = max((red, green, blue))
black = 1.0 - white
return [
# Each channel is it's color chart oposite (cyan->red)
# with a bit of white removed.
(white and (1.0 - red - black) / white or 0.0),
(white and (1.0 - green - black) / white or 0.0),
(white and (1.0 - blue - black) / white or 0.0),
black,
] + list(alpha)

139
deps/inkex/colors/spaces/css.py vendored Normal file
View File

@@ -0,0 +1,139 @@
# coding=utf-8
#
# Copyright (C) 2018-2024 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# pylint: disable=W0223
"""
Parsing CSS elements from colors
"""
from typing import Optional, Union
from ..color import Color, ColorError, ColorIdError
class CssColor(Color):
"""
A Color which is always parsed and printed from a css format.
"""
# A list of css prefixes which ar valid for this space
css_noalpha_prefix: Optional[str] = None
css_alpha_prefix: Optional[str] = None
css_either_prefix: Optional[str] = None
# Some CSS formats require commas, others do not
css_join: str = ", "
css_join_alpha: str = ", "
css_func = "color"
def __str__(self):
values = self.css_join.join([f"{v:g}" for v in self.get_css_values()])
prefix = self.css_noalpha_prefix or self.css_either_prefix
if self.alpha is not None:
# Alpha is stored as a percent for clarity
alpha = int(self.alpha * 100)
values += self.css_join_alpha + f"{alpha}%"
if not self.css_either_prefix:
prefix = self.css_alpha_prefix
if prefix is None:
raise ColorError(f"Can't encode color {self.name} into CSS color format.")
return f"{prefix}({values})"
@classmethod
def can_parse(cls, string: str):
string = string.replace(" ", "")
if "(" not in string or ")" not in string:
return False
for prefix in (
cls.css_noalpha_prefix,
cls.css_alpha_prefix,
cls.css_either_prefix,
):
if prefix and (prefix + "(" in string or "color(" + prefix in string):
return True
return False
def __init__(self, value, alpha=None):
if isinstance(value, str):
prefix, values = self.parse_css_color(value)
has_alpha = (
self.channels is not None and len(values) == len(self.channels) + 1
)
if prefix == self.css_noalpha_prefix or (
prefix == self.css_either_prefix and not has_alpha
):
super().__init__(values)
elif prefix == self.css_alpha_prefix or (
prefix == self.css_either_prefix and has_alpha
):
super().__init__(values, values.pop())
else:
raise ColorError(f"Could not parse {self.name} css color: '{value}'")
else:
super().__init__(value, alpha=alpha)
@classmethod
def parse_css_color(cls, value):
"""Parse a css string into a list of values and it's color space prefix"""
prefix, values = value.lower().strip().strip(")").split("(")
# Some css formats use commas, others do not
if "," in cls.css_join:
values = values.replace(",", " ")
if "/" in cls.css_join_alpha:
values = values.replace("/", " ")
# Split values by spaces
values = values.split()
prefix = prefix.strip()
if prefix == cls.css_func:
prefix = values.pop(0)
if prefix == "url":
raise ColorIdError("Can not parse url as if it was a color.")
return prefix, [cls.parse_css_value(i, v) for i, v in enumerate(values)]
def get_css_values(self):
"""Return a list of values used for css string output"""
return self
@classmethod
def parse_css_value(cls, index, value) -> Union[int, float]:
"""Parse a CSS value such as 100%, 360 or 0.4"""
if cls.scales and index >= len(cls.scales):
raise ValueError("Can't add any more values to color.")
if isinstance(value, str):
value = value.strip()
if value.endswith("%"):
value = float(value.strip("%")) / 100
elif "." in value:
value = float(value)
else:
value = int(value)
if isinstance(value, float) and value <= 1.0:
value = cls.scale_up(index, value)
return cls.constrain(index, value)
class CssColorModule4(CssColor):
"""Tweak the css parser for CSS Module Four formating"""
css_join = " "
css_join_alpha = " / "

107
deps/inkex/colors/spaces/hsl.py vendored Normal file
View File

@@ -0,0 +1,107 @@
# coding=utf-8
#
# Copyright (C) 2024 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# pylint: disable=W0223
"""
HSL Color Space
"""
from .css import CssColor
class ColorHSL(CssColor):
"""
Parse the HSL CSS Module Module 3 format.
"""
name = "hsl"
channels = ("hue", "saturation", "lightness")
scales = ((0, 360, True), (0, 100), (0, 100), (0.0, 1.0))
css_noalpha_prefix = "hsl"
css_alpha_prefix = "hsla"
hue = property(lambda self: self[0], lambda self, value: self.__setitem__(0, value))
saturation = property(
lambda self: self[1], lambda self, value: self.__setitem__(1, value)
)
lightness = property(
lambda self: self[2], lambda self, value: self.__setitem__(2, value)
)
@staticmethod
def convert_from_rgb(red, green, blue, alpha=None):
"""RGB to HSL colour conversion"""
rgb_max = max(red, green, blue)
rgb_min = min(red, green, blue)
delta = rgb_max - rgb_min
hsl = [0.0, 0.0, (rgb_max + rgb_min) / 2.0]
if delta != 0:
if hsl[2] <= 0.5:
hsl[1] = delta / (rgb_max + rgb_min)
else:
hsl[1] = delta / (2 - rgb_max - rgb_min)
if red == rgb_max:
hsl[0] = (green - blue) / delta
elif green == rgb_max:
hsl[0] = 2.0 + (blue - red) / delta
elif blue == rgb_max:
hsl[0] = 4.0 + (red - green) / delta
hsl[0] /= 6.0
if hsl[0] < 0:
hsl[0] += 1
if hsl[0] > 1:
hsl[0] -= 1
if alpha is not None:
hsl.append(alpha)
return hsl
@staticmethod
def convert_to_rgb(hue, sat, light, *alpha):
"""HSL to RGB Color Conversion"""
if sat == 0:
return [light, light, light] # Gray
if light < 0.5:
val2 = light * (1 + sat)
else:
val2 = light + sat - light * sat
val1 = 2 * light - val2
ret = [
_hue_to_rgb(val1, val2, hue * 6 + 2.0),
_hue_to_rgb(val1, val2, hue * 6),
_hue_to_rgb(val1, val2, hue * 6 - 2.0),
]
return ret + list(alpha)
def _hue_to_rgb(val1, val2, hue):
if hue < 0:
hue += 6.0
if hue > 6:
hue -= 6.0
if hue < 1:
return val1 + (val2 - val1) * hue
if hue < 3:
return val2
if hue < 4:
return val1 + (val2 - val1) * (4 - hue)
return val1

88
deps/inkex/colors/spaces/hsv.py vendored Normal file
View File

@@ -0,0 +1,88 @@
# coding=utf-8
#
# Copyright (C) 2024 Jonathan Neuhauser
# 2024 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# pylint: disable=W0223
"""
HSV Color Space
"""
from .css import CssColorModule4
class ColorHSV(CssColorModule4):
"""
Parse the HWB CSS Color Module 4 format and retain as HSV values.
"""
name = "hsv"
channels = ("hue", "saturation", "value")
scales = ((0, 360, True), (0, 100), (0, 100), (0.0, 1.0))
# We use HWB to store HSV as this makes the most sense to Inkscape
css_either_prefix = "hwb"
hue = property(lambda self: self[0], lambda self, value: self.__setitem__(0, value))
saturation = property(
lambda self: self[1], lambda self, value: self.__setitem__(1, value)
)
value = property(
lambda self: self[2], lambda self, value: self.__setitem__(2, value)
)
@classmethod
def parse_css_color(cls, value):
"""Parsing HWB as if it was HSV for css input"""
prefix, values = super().parse_css_color(value)
# See https://en.wikipedia.org/wiki/HWB_color_model#Converting_to_and_from_HSV
values[1] /= 100
values[2] /= 100
scale = values[1] + values[2]
if scale > 1.0:
values[1] /= scale
values[2] /= scale
values[1] = int(
(values[2] == 1.0 and 0.0 or (1.0 - (values[1] / (1.0 - values[2])))) * 100
)
values[2] = int((1.0 - values[2]) * 100)
return prefix, values
def get_css_values(self):
"""Convert our HSV values into HWB for css output"""
values = list(self)
values[1] = (100 - values[1]) * (values[2] / 100)
values[2] = 100 - values[2]
return values
@staticmethod
def convert_to_hsl(hue, saturation, value, *alpha):
"""Conversion according to
https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_HSL
.. versionadded:: 1.5"""
lum = value * (1 - saturation / 2)
sat = 0 if lum in (0, 1) else (value - lum) / min(lum, 1 - lum)
return [hue, sat, lum] + list(alpha)
@staticmethod
def convert_from_hsl(hue, saturation, lightness, *alpha):
"""Convertion according to Inkscape C++ codebase
.. versionadded:: 1.5"""
val = lightness + saturation * min(lightness, 1 - lightness)
sat = 0 if val == 0 else 2 * (1 - lightness / val)
return [hue, sat, val] + list(alpha)

236
deps/inkex/colors/spaces/named.py vendored Normal file
View File

@@ -0,0 +1,236 @@
# coding=utf-8
#
# Copyright (C) 2024, Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
"""
CSS Named colors
"""
from typing import Dict
from ..color import Color
from .rgb import ColorRGB
_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",
"gold": "#ffd700",
"goldenrod": "#daa520",
"gray": "#808080",
"grey": "#808080",
"green": "#008000",
"greenyellow": "#adff2f",
"honeydew": "#f0fff0",
"hotpink": "#ff69b4",
"indianred": "#cd5c5c",
"indigo": "#4b0082",
"ivory": "#fffff0",
"khaki": "#f0e68c",
"lavender": "#e6e6fa",
"lavenderblush": "#fff0f5",
"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",
}
class ColorNamed(ColorRGB):
"""
Parse specific named colors, fall back to RGB parsing if it fails.
"""
_color_names: Dict[ColorRGB, str] = {}
_name_colors: Dict[str, ColorRGB] = {}
name = "named"
def __init__(self, name, alpha=None):
if isinstance(name, str):
super().__init__(self.name_colors()[name.lower().strip()])
else:
super().__init__(name, alpha=alpha)
@classmethod
def color_names(cls):
"""Cache a list of color names"""
if not cls._color_names:
cls._color_names = {
value: name for name, value in cls.name_colors().items()
}
return cls._color_names
@classmethod
def name_colors(cls):
"""Cache a list of color objects"""
if not cls._name_colors:
cls._name_colors = {name: Color(value) for name, value in _COLORS.items()}
return cls._name_colors
def __str__(self):
return self.color_names().get(self, super().__str__())
def __hash__(self):
"""Allow named colors to match rgb colors"""
return tuple(self + [self.alpha, super().name]).__hash__()
@classmethod
def can_parse(cls, string: str):
"""If the string is one of the color names, we can parse it"""
return string in cls.name_colors()
@staticmethod
def convert_to_rgb(*data):
"""Converting to RGB is transparent, already in RGB"""
return data
@staticmethod
def convert_from_rgb(*data):
"""Converting from RGB is transparent, the store is RGB"""
return data
def to_rgb(self):
"""Prevent masking by ColorRGB of to_rgb method"""
return ColorRGB(list(self), alpha=self.alpha)

55
deps/inkex/colors/spaces/none.py vendored Normal file
View File

@@ -0,0 +1,55 @@
# coding=utf-8
#
# Copyright (C) 2021 Jonathan Neuhauser
# 2020 Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# pylint: disable=W0223
"""
An empty color for 'none'
"""
from ..color import Color, AlphaNotAllowed
class ColorNone(Color, AlphaNotAllowed):
"""A special color for 'none' colors"""
name = "none"
# Override opacity since none can not have opacity
default_alpha = 0.0
def __init__(self, value=None):
pass
def __str__(self) -> str:
return "none"
@classmethod
def can_parse(cls, string: str) -> bool:
"""Returns true if this is the word 'none'"""
return string == "none"
@staticmethod
def convert_to_rgb(*_):
"""Converting to RGB means transparent black"""
return [0, 0, 0, 0]
@staticmethod
def convert_from_rgb(*_):
"""Converting from RGB means throwing out all data"""
return []

105
deps/inkex/colors/spaces/rgb.py vendored Normal file
View File

@@ -0,0 +1,105 @@
# coding=utf-8
#
# Copyright (C) 2024, Martin Owens
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
"""
RGB Colors
"""
from ..color import ColorError
from .css import CssColor
class ColorRGB(CssColor):
"""
Parse multiple versions of RGB from CSS module and standard hex formats.
"""
name = "rgb"
channels = ("red", "green", "blue")
scales = ((0, 255), (0, 255), (0, 255), (0.0, 1.0))
css_noalpha_prefix = "rgb"
css_alpha_prefix = "rgba"
red = property(lambda self: self[0], lambda self, value: self.__setitem__(0, value))
green = property(
lambda self: self[1], lambda self, value: self.__setitem__(1, value)
)
blue = property(
lambda self: self[2], lambda self, value: self.__setitem__(2, value)
)
@classmethod
def can_parse(cls, string: str) -> bool:
return "icc" not in string and (
string.startswith("#")
or string.lstrip("-").isdigit()
or super().can_parse(string)
)
def __init__(self, value, alpha=None):
# Not CSS, but inkscape, some old color values stores as 32bit int strings
if isinstance(value, str) and value.lstrip("-").isdigit():
value = int(value)
if isinstance(value, int):
super().__init__(
[
((value >> 24) & 255), # red
((value >> 16) & 255), # green
((value >> 8) & 255), # blue
((value & 255) / 255.0),
]
) # opacity
elif isinstance(value, str) and value.startswith("#") and " " not in value:
if len(value) == 4: # (css: #rgb -> #rrggbb)
# pylint: disable=consider-using-f-string
value = "#{1}{1}{2}{2}{3}{3}".format(*value)
elif len(value) == 5: # (css: #rgba -> #rrggbbaa)
# pylint: disable=consider-using-f-string
value = "#{1}{1}{2}{2}{3}{3}{4}{4}".format(*value)
# Convert hex to integers
try:
values = [int(value[i : i + 2], 16) for i in range(1, len(value), 2)]
if len(values) == 4:
values[3] /= 255
super().__init__(values)
except ValueError as error:
raise ColorError(f"Bad RGB hex color value '{value}'") from error
else:
super().__init__(value, alpha=alpha)
def __str__(self) -> str:
if self.alpha is not None:
return super().__str__()
if len(self) < len(self.channels):
raise ColorError(
f"Incorrect number of channels for Color Space {self.name}"
)
# Always hex values when outputting color
return "#{0:02x}{1:02x}{2:02x}".format(*(int(v) for v in self)) # pylint: disable=consider-using-f-string
def __int__(self) -> int:
return (
(self[0] << 24)
+ (self[1] << 16)
+ (self[2] << 8)
+ int((self.alpha or 1.0) * 255)
)