use std::fmt::Debug;
use std::hash::Hash;
use crate::configure::ValidationError;
use crate::graphics::icc::ICCBasedColorSpace;
use crate::serialize::SerializeContext;
pub(crate) const DEVICE_RGB: &str = "DeviceRGB";
pub(crate) const DEVICE_GRAY: &str = "DeviceGray";
pub(crate) const DEVICE_CMYK: &str = "DeviceCMYK";
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub enum Color {
Regular(RegularColor),
Special(SpecialColor),
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub enum RegularColor {
Rgb(rgb::Color),
Luma(luma::Color),
Cmyk(cmyk::Color),
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub enum SpecialColor {
Separation(separation::Color),
}
impl Color {
pub(crate) fn to_pdf_color(&self) -> Vec<f32> {
match self {
Color::Regular(RegularColor::Rgb(rgb)) => rgb.to_pdf_color().to_vec(),
Color::Regular(RegularColor::Luma(l)) => vec![l.to_pdf_color()],
Color::Regular(RegularColor::Cmyk(cmyk)) => cmyk.to_pdf_color().to_vec(),
Color::Special(SpecialColor::Separation(spot)) => vec![spot.to_pdf_color()],
}
}
pub(crate) fn color_space(&self, sc: &mut SerializeContext) -> ColorSpace {
match self {
Color::Regular(c) => c.color_space(sc).into(),
Color::Special(c) => c.color_space().into(),
}
}
pub(crate) fn to_regular(&self) -> RegularColor {
match self {
Color::Regular(c) => *c,
Color::Special(SpecialColor::Separation(c)) => c.space.fallback,
}
}
}
impl RegularColor {
pub(crate) fn color_space(&self, sc: &mut SerializeContext) -> RegularColorSpace {
match self {
Self::Rgb(r) => r.color_space(sc.serialize_settings().no_device_cs),
Self::Luma(_) => luma::color_space(sc.serialize_settings().no_device_cs),
Self::Cmyk(_) => match cmyk::color_space(&sc.serialize_settings()) {
None => {
sc.register_validation_error(ValidationError::MissingCMYKProfile);
DeviceColorSpace::Cmyk.into()
}
Some(cs) => cs,
},
}
}
pub(crate) fn as_rgb(self) -> Option<rgb::Color> {
Some(match self {
Self::Rgb(r) => r,
Self::Luma(l) => rgb::Color::new(l.0, l.0, l.0),
Self::Cmyk(_) => return None,
})
}
pub(crate) fn is_subtractive(self) -> bool {
matches!(self, Self::Cmyk(_))
}
}
impl SpecialColor {
pub(crate) fn color_space(&self) -> SpecialColorSpace {
match self {
Self::Separation(spot) => spot.color_space().into(),
}
}
}
pub mod luma {
use crate::color::{CieBasedColorSpace, DeviceColorSpace, RegularColor, RegularColorSpace};
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub struct Color(pub(crate) u8);
impl Color {
pub fn new(lightness: u8) -> Color {
Color(lightness)
}
pub fn black() -> Self {
Self::new(0)
}
pub fn white() -> Self {
Self::new(255)
}
pub(crate) fn to_pdf_color(self) -> f32 {
self.0 as f32 / 255.0
}
}
impl From<Color> for super::RegularColor {
fn from(val: Color) -> Self {
super::RegularColor::Luma(val)
}
}
impl From<Color> for super::Color {
fn from(val: Color) -> Self {
RegularColor::from(val).into()
}
}
impl Default for Color {
fn default() -> Self {
Color::new(0)
}
}
pub(crate) fn color_space(no_device_cs: bool) -> RegularColorSpace {
if no_device_cs {
CieBasedColorSpace::Luma.into()
} else {
DeviceColorSpace::Gray.into()
}
}
}
impl From<RegularColor> for Color {
fn from(value: RegularColor) -> Self {
Self::Regular(value)
}
}
impl From<SpecialColor> for Color {
fn from(value: SpecialColor) -> Self {
Self::Special(value)
}
}
pub mod cmyk {
use crate::color::{CieBasedColorSpace, DeviceColorSpace, RegularColorSpace};
use crate::graphics::icc::ICCBasedColorSpace;
use crate::SerializeSettings;
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub struct Color(pub(crate) u8, pub(crate) u8, pub(crate) u8, pub(crate) u8);
impl Color {
pub fn new(cyan: u8, magenta: u8, yellow: u8, black: u8) -> Color {
Color(cyan, magenta, yellow, black)
}
pub(crate) fn to_pdf_color(self) -> [f32; 4] {
[
self.0 as f32 / 255.0,
self.1 as f32 / 255.0,
self.2 as f32 / 255.0,
self.3 as f32 / 255.0,
]
}
}
impl From<Color> for super::RegularColor {
fn from(val: Color) -> Self {
super::RegularColor::Cmyk(val)
}
}
impl From<Color> for super::Color {
fn from(val: Color) -> Self {
super::RegularColor::from(val).into()
}
}
impl Default for Color {
fn default() -> Self {
Color::new(0, 0, 0, 255)
}
}
pub(crate) fn color_space(ss: &SerializeSettings) -> Option<RegularColorSpace> {
if ss.no_device_cs {
ss.clone()
.cmyk_profile
.map(|p| CieBasedColorSpace::Cmyk(ICCBasedColorSpace::<4>(p.clone())).into())
} else {
Some(DeviceColorSpace::Cmyk.into())
}
}
}
pub mod rgb {
use crate::color::{CieBasedColorSpace, DeviceColorSpace, RegularColorSpace};
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
pub struct Color(pub(crate) u8, pub(crate) u8, pub(crate) u8);
impl Default for Color {
fn default() -> Self {
Color::black()
}
}
impl Color {
pub fn new(red: u8, green: u8, blue: u8) -> Self {
Color(red, green, blue)
}
pub fn new_linear(red: u8, green: u8, blue: u8) -> Self {
Color(red, green, blue)
}
pub fn black() -> Self {
Self::new(0, 0, 0)
}
pub fn white() -> Self {
Self::new(255, 255, 255)
}
pub fn red(&self) -> u8 {
self.0
}
pub fn green(&self) -> u8 {
self.1
}
pub fn blue(&self) -> u8 {
self.2
}
pub(crate) fn to_pdf_color(self) -> [f32; 3] {
[
self.0 as f32 / 255.0,
self.1 as f32 / 255.0,
self.2 as f32 / 255.0,
]
}
pub(super) fn color_space(&self, no_device_cs: bool) -> RegularColorSpace {
color_space(no_device_cs)
}
}
impl From<Color> for super::RegularColor {
fn from(val: Color) -> Self {
super::RegularColor::Rgb(val)
}
}
impl From<Color> for super::Color {
fn from(val: Color) -> Self {
super::RegularColor::from(val).into()
}
}
pub(crate) fn color_space(no_device_cs: bool) -> RegularColorSpace {
if no_device_cs {
CieBasedColorSpace::Srgb.into()
} else {
DeviceColorSpace::Rgb.into()
}
}
}
pub mod separation {
use crate::color::RegularColor;
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct Color {
pub(crate) tint: u8,
pub(crate) space: SeparationSpace,
}
impl Color {
pub fn new(tint: u8, space: SeparationSpace) -> Self {
Self { tint, space }
}
pub(crate) fn to_pdf_color(&self) -> f32 {
self.tint as f32 / 255.0
}
pub(crate) fn color_space(&self) -> SeparationSpace {
self.space.clone()
}
}
impl Default for Color {
fn default() -> Self {
Color::new(0, SeparationSpace::default())
}
}
impl From<Color> for super::SpecialColor {
fn from(val: Color) -> Self {
super::SpecialColor::Separation(val)
}
}
impl From<Color> for super::Color {
fn from(val: Color) -> Self {
super::SpecialColor::from(val).into()
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct SeparationSpace {
pub(crate) colorant: SeparationColorant,
pub(crate) fallback: RegularColor,
}
impl SeparationSpace {
pub fn new(colorant: SeparationColorant, fallback: RegularColor) -> Self {
Self { colorant, fallback }
}
}
impl From<SeparationSpace> for super::SpecialColorSpace {
fn from(value: SeparationSpace) -> Self {
Self::Separation(value)
}
}
impl Default for SeparationSpace {
fn default() -> Self {
Self {
colorant: SeparationColorant::default(),
fallback: super::rgb::Color::default().into(),
}
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone, Default)]
pub enum SeparationColorant {
#[default]
NoColorant,
AllColorants,
Custom(String),
}
impl SeparationColorant {
pub(crate) fn to_pdf<'a>(&'a self) -> pdf_writer::Name<'a> {
match self {
Self::AllColorants => pdf_writer::Name(b"All"),
Self::NoColorant => pdf_writer::Name(b"None"),
Self::Custom(s) => pdf_writer::Name(s.as_bytes()),
}
}
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum ColorSpace {
Device(DeviceColorSpace),
CieBased(CieBasedColorSpace),
Special(SpecialColorSpace),
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum RegularColorSpace {
Device(DeviceColorSpace),
CieBased(CieBasedColorSpace),
}
impl From<RegularColorSpace> for ColorSpace {
fn from(value: RegularColorSpace) -> Self {
match value {
RegularColorSpace::Device(s) => Self::Device(s),
RegularColorSpace::CieBased(s) => Self::CieBased(s),
}
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum DeviceColorSpace {
Rgb,
Gray,
Cmyk,
}
impl From<DeviceColorSpace> for ColorSpace {
fn from(value: DeviceColorSpace) -> Self {
Self::Device(value)
}
}
impl From<DeviceColorSpace> for RegularColorSpace {
fn from(value: DeviceColorSpace) -> Self {
Self::Device(value)
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum CieBasedColorSpace {
Srgb,
Luma,
Cmyk(ICCBasedColorSpace<4>),
}
impl From<CieBasedColorSpace> for ColorSpace {
fn from(value: CieBasedColorSpace) -> Self {
Self::CieBased(value)
}
}
impl From<CieBasedColorSpace> for RegularColorSpace {
fn from(value: CieBasedColorSpace) -> Self {
Self::CieBased(value)
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum SpecialColorSpace {
Separation(separation::SeparationSpace),
}
impl From<SpecialColorSpace> for ColorSpace {
fn from(value: SpecialColorSpace) -> Self {
Self::Special(value)
}
}