use crate::{
is_empty_or_none, is_zero, round_to_precision, signatures::Signature, tag::RenderingIntent,
};
pub mod device_attributes {
pub const TRANSPARENCY: u64 = 1 << 0; pub const MATTE: u64 = 1 << 1; pub const NEGATIVE: u64 = 1 << 2; pub const BLACK_AND_WHITE: u64 = 1 << 3;
#[cfg(feature = "v5")]
pub const DEVICE_MEDIA: u64 = 1 << 4; #[cfg(feature = "v5")]
pub const BCS_INTENT: u64 = 1 << 5; }
#[derive(serde::Serialize)]
pub struct Header {
profile_size: u32,
cmm: Option<String>,
version: String,
#[serde(skip_serializing_if = "is_empty_or_none")]
device_class: String,
color_space: Option<String>,
pcs: String,
creation_datetime: String,
primary_platform: Option<String>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
embedded: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
use_embedded_only: bool,
manufacturer: Option<String>,
#[serde(skip_serializing_if = "is_empty_or_none")]
model: String,
#[serde(skip_serializing_if = "is_empty_or_none")]
attributes: String,
rendering_intent: String,
pcs_illuminant: (f64, f64, f64),
creator: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
profile_id: String,
}
impl From<&super::RawProfile> for Header {
fn from(raw_profile: &super::RawProfile) -> Self {
let header = raw_profile.header();
let (major, minor) = raw_profile.version().unwrap();
let version = format!("{major}.{minor}");
let (embedded, use_embedded_only) = raw_profile.flags();
let profile_id_raw = raw_profile.profile_id();
let profile_id = if profile_id_raw.iter().all(is_zero) {
String::new()
} else {
hex::encode(profile_id_raw)
};
let model = if header.model == 0 {
String::new()
} else {
Signature(header.model.get()).to_string()
};
let manufacturer = raw_profile.manufacturer().map(|m| m.to_string());
use self::device_attributes::*;
let attrs_u64 = header.attributes.get();
let mut attr_strings = Vec::new();
if attrs_u64 & TRANSPARENCY != 0 {
attr_strings.push("Transparency");
}
if attrs_u64 & MATTE != 0 {
attr_strings.push("Matte");
}
if attrs_u64 & NEGATIVE != 0 {
attr_strings.push("Negative");
}
if attrs_u64 & BLACK_AND_WHITE != 0 {
attr_strings.push("BlackAndWhite");
}
#[cfg(feature = "v5")]
{
if attrs_u64 & DEVICE_MEDIA != 0 {
attr_strings.push("DeviceMedia");
}
if attrs_u64 & BCS_INTENT != 0 {
attr_strings.push("BCSIntent");
}
}
let attributes_string = if attr_strings.is_empty() {
String::new()
} else {
attr_strings.join(", ")
};
Header {
profile_size: raw_profile.profile_size() as u32,
cmm: raw_profile.cmm().map(|c| c.to_string()),
version,
device_class: raw_profile.device_class().to_string(),
color_space: raw_profile.data_color_space().map(|c| c.to_string()),
pcs: raw_profile.pcs().unwrap().to_string(),
creation_datetime: raw_profile.creation_date().to_string(),
primary_platform: raw_profile.primary_platform().map(|c| c.to_string()),
embedded,
use_embedded_only,
manufacturer,
model,
attributes: attributes_string,
rendering_intent: RenderingIntent::from(header.rendering_intent.get()).to_string(),
pcs_illuminant: (
round_to_precision(f64::from(header.pcs_illuminant[0]), 4),
round_to_precision(f64::from(header.pcs_illuminant[1]), 4),
round_to_precision(f64::from(header.pcs_illuminant[2]), 4),
),
creator: raw_profile.creator().map(|c| c.to_string()),
profile_id,
}
}
}