use std::fmt;
mod raw_profile;
pub use raw_profile::{ProfileTagRecord, RawProfile};
use indexmap::IndexMap;
mod input_profile;
pub use input_profile::InputProfile;
mod display_profile;
pub use display_profile::DisplayProfile;
mod output_profile;
pub use output_profile::OutputProfile;
mod device_link_profile;
pub use device_link_profile::DeviceLinkProfile;
mod abstract_profile;
pub use abstract_profile::AbstractProfile;
mod color_space_profile;
pub use color_space_profile::ColorSpaceProfile;
mod named_color_profile;
pub use named_color_profile::NamedColorProfile;
mod spectral_profile;
use serde::Serialize;
pub use spectral_profile::SpectralProfile;
mod delegate;
mod tag_setter;
mod with_tag;
pub use tag_setter::TagSetter;
mod checksum;
use crate::{header::Header, tag::ParsedTag};
pub use {checksum::md5checksum, checksum::set_profile_id};
#[derive(Debug)]
pub enum Profile {
Input(InputProfile),
Display(DisplayProfile),
Output(OutputProfile),
DeviceLink(DeviceLinkProfile),
Abstract(AbstractProfile),
ColorSpace(ColorSpaceProfile),
NamedColor(NamedColorProfile),
Spectral(SpectralProfile),
Raw(RawProfile),
}
impl Profile {
fn into_raw_profile(self) -> RawProfile {
match self {
Profile::Input(p) => p.0,
Profile::Display(p) => p.0,
Profile::Output(p) => p.0,
Profile::DeviceLink(p) => p.0,
Profile::Abstract(p) => p.0,
Profile::ColorSpace(p) => p.0,
Profile::NamedColor(p) => p.0,
Profile::Spectral(p) => p.0,
Profile::Raw(p) => p,
}
}
fn as_raw_profile(&self) -> &RawProfile {
match self {
Profile::Input(p) => &p.0,
Profile::Display(p) => &p.0,
Profile::Output(p) => &p.0,
Profile::DeviceLink(p) => &p.0,
Profile::Abstract(p) => &p.0,
Profile::ColorSpace(p) => &p.0,
Profile::NamedColor(p) => &p.0,
Profile::Spectral(p) => &p.0,
Profile::Raw(p) => p,
}
}
fn as_raw_profile_mut(&mut self) -> &mut RawProfile {
match self {
Profile::Input(p) => &mut p.0,
Profile::Display(p) => &mut p.0,
Profile::Output(p) => &mut p.0,
Profile::DeviceLink(p) => &mut p.0,
Profile::Abstract(p) => &mut p.0,
Profile::ColorSpace(p) => &mut p.0,
Profile::NamedColor(p) => &mut p.0,
Profile::Spectral(p) => &mut p.0,
Profile::Raw(p) => p,
}
}
pub fn version(&self) -> Result<(u8, u8), crate::Error> {
self.as_raw_profile().version()
}
pub fn profile_size(&self) -> usize {
self.as_raw_profile().profile_size()
}
pub fn flags(&self) -> (bool, bool) {
self.as_raw_profile().flags()
}
pub fn data_color_space(&self) -> Option<crate::signatures::ColorSpace> {
self.as_raw_profile().data_color_space()
}
pub fn primary_platform(&self) -> Option<crate::signatures::Platform> {
self.as_raw_profile().primary_platform()
}
pub fn manufacturer(&self) -> Option<crate::signatures::Signature> {
self.as_raw_profile().manufacturer()
}
pub fn model(&self) -> crate::signatures::Signature {
self.as_raw_profile().model()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
let raw = RawProfile::from_bytes(bytes)?;
Ok(raw.into_class_profile())
}
pub fn read(path: impl AsRef<std::path::Path>) -> Result<Self, Box<dyn std::error::Error>> {
let raw = RawProfile::read(path)?;
Ok(raw.into_class_profile())
}
pub fn into_bytes(self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
self.into_raw_profile().into_bytes()
}
pub fn write(
self,
path: impl AsRef<std::path::Path>,
) -> Result<(), Box<dyn std::error::Error>> {
self.into_raw_profile().write(path)
}
pub fn profile_id(&self) -> [u8; 16] {
self.as_raw_profile().profile_id()
}
pub fn profile_id_as_hex_string(&self) -> String {
self.as_raw_profile().profile_id_as_hex_string()
}
pub fn creation_date(&self) -> chrono::DateTime<chrono::Utc> {
self.as_raw_profile().creation_date()
}
pub fn pcs_illuminant(&self) -> [f64; 3] {
self.as_raw_profile().pcs_illuminant()
}
pub fn pcs(&self) -> Option<crate::signatures::Pcs> {
self.as_raw_profile().pcs()
}
pub fn cmm(&self) -> Option<crate::signatures::Cmm> {
self.as_raw_profile().cmm()
}
pub fn with_version(self, major: u8, minor: u8) -> Result<Self, crate::Error> {
Ok(match self {
Profile::Input(p) => Profile::Input(p.with_version(major, minor)?),
Profile::Display(p) => Profile::Display(p.with_version(major, minor)?),
Profile::Output(p) => Profile::Output(p.with_version(major, minor)?),
Profile::DeviceLink(p) => Profile::DeviceLink(p.with_version(major, minor)?),
Profile::Abstract(p) => Profile::Abstract(p.with_version(major, minor)?),
Profile::ColorSpace(p) => Profile::ColorSpace(p.with_version(major, minor)?),
Profile::NamedColor(p) => Profile::NamedColor(p.with_version(major, minor)?),
Profile::Spectral(p) => Profile::Spectral(p.with_version(major, minor)?),
Profile::Raw(p) => Profile::Raw(p.with_version(major, minor)?),
})
}
pub fn with_creation_date(self, date: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
match self {
Profile::Input(p) => Profile::Input(p.with_creation_date(date)),
Profile::Display(p) => Profile::Display(p.with_creation_date(date)),
Profile::Output(p) => Profile::Output(p.with_creation_date(date)),
Profile::DeviceLink(p) => Profile::DeviceLink(p.with_creation_date(date)),
Profile::Abstract(p) => Profile::Abstract(p.with_creation_date(date)),
Profile::ColorSpace(p) => Profile::ColorSpace(p.with_creation_date(date)),
Profile::NamedColor(p) => Profile::NamedColor(p.with_creation_date(date)),
Profile::Spectral(p) => Profile::Spectral(p.with_creation_date(date)),
Profile::Raw(p) => Profile::Raw(p.with_creation_date(date)),
}
}
pub fn with_tag<S: Into<crate::tag::TagSignature> + Copy>(
self,
signature: S,
) -> TagSetter<Self, S> {
TagSetter::new(self, signature)
}
}
fn write_toml_from_raw(raw: &RawProfile, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let header = Header::from(raw);
let tags: IndexMap<String, ParsedTag> = raw
.tags
.iter()
.map(|(sig, entry)| (sig.to_string(), entry.tag.to_parsed()))
.collect();
let parsed_profile = ParsedProfile { header, tags };
match toml::to_string(&parsed_profile) {
Ok(s) => write!(f, "{s}"),
Err(_) => Err(fmt::Error),
}
}
impl fmt::Display for Profile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_toml_from_raw(self.as_raw_profile(), f)
}
}
macro_rules! impl_display_for_wrappers {
($($ty:ty),+ $(,)?) => {
$(
impl fmt::Display for $ty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_toml_from_raw(&self.0, f)
}
}
)+
};
}
impl_display_for_wrappers!(
InputProfile,
DisplayProfile,
OutputProfile,
DeviceLinkProfile,
AbstractProfile,
ColorSpaceProfile,
NamedColorProfile,
SpectralProfile,
);
#[derive(Serialize)]
pub struct ParsedProfile {
#[serde(flatten)]
pub header: Header,
#[serde(flatten)]
pub tags: IndexMap<String, ParsedTag>,
}
#[cfg(test)]
mod test {
use crate::profile::RawProfile;
#[test]
fn print_rgb() -> Result<(), Box<dyn std::error::Error>> {
let profile = include_bytes!("../tests/profiles/sRGB.icc");
let raw_profile = RawProfile::from_bytes(profile).unwrap();
println!("{}", crate::profile::Profile::Raw(raw_profile));
Ok(())
}
#[test]
fn print_display_p3() -> Result<(), Box<dyn std::error::Error>> {
let profile = include_bytes!("../tests/profiles/Display P3.icc");
let raw_profile = RawProfile::from_bytes(profile).unwrap();
println!("{}", crate::profile::Profile::Raw(raw_profile));
Ok(())
}
}