initial commit
This commit is contained in:
11
deps/inkex/colors/spaces/__init__.py
vendored
Normal file
11
deps/inkex/colors/spaces/__init__.py
vendored
Normal 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
|
||||
BIN
deps/inkex/colors/spaces/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/__init__.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/__init__.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/cms.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/cms.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/cms.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/cms.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/cmyk.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/cmyk.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/cmyk.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/cmyk.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/css.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/css.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/css.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/css.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/hsl.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/hsl.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/hsl.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/hsl.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/hsv.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/hsv.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/hsv.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/hsv.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/named.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/named.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/named.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/named.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/none.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/none.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/none.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/none.cpython-313.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/rgb.cpython-312.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/rgb.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
deps/inkex/colors/spaces/__pycache__/rgb.cpython-313.pyc
vendored
Normal file
BIN
deps/inkex/colors/spaces/__pycache__/rgb.cpython-313.pyc
vendored
Normal file
Binary file not shown.
95
deps/inkex/colors/spaces/cms.py
vendored
Normal file
95
deps/inkex/colors/spaces/cms.py
vendored
Normal 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
81
deps/inkex/colors/spaces/cmyk.py
vendored
Normal 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
139
deps/inkex/colors/spaces/css.py
vendored
Normal 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
107
deps/inkex/colors/spaces/hsl.py
vendored
Normal 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
88
deps/inkex/colors/spaces/hsv.py
vendored
Normal 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
236
deps/inkex/colors/spaces/named.py
vendored
Normal 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
55
deps/inkex/colors/spaces/none.py
vendored
Normal 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
105
deps/inkex/colors/spaces/rgb.py
vendored
Normal 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)
|
||||
)
|
||||
Reference in New Issue
Block a user