use super::state::BlendMode;
use crate::objects::{Dictionary, Object};
#[derive(Debug, Clone)]
pub struct TransparencyGroup {
pub isolated: bool,
pub knockout: bool,
pub blend_mode: BlendMode,
pub opacity: f32,
pub color_space: Option<String>,
}
impl Default for TransparencyGroup {
fn default() -> Self {
Self {
isolated: false,
knockout: false,
blend_mode: BlendMode::Normal,
opacity: 1.0,
color_space: None,
}
}
}
impl TransparencyGroup {
pub fn new() -> Self {
Self::default()
}
pub fn isolated() -> Self {
Self {
isolated: true,
..Default::default()
}
}
pub fn knockout() -> Self {
Self {
knockout: true,
..Default::default()
}
}
pub fn with_isolated(mut self, isolated: bool) -> Self {
self.isolated = isolated;
self
}
pub fn with_knockout(mut self, knockout: bool) -> Self {
self.knockout = knockout;
self
}
pub fn with_blend_mode(mut self, blend_mode: BlendMode) -> Self {
self.blend_mode = blend_mode;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn with_color_space(mut self, color_space: impl Into<String>) -> Self {
self.color_space = Some(color_space.into());
self
}
pub fn to_dict(&self) -> Dictionary {
let mut dict = Dictionary::new();
dict.set("Type", Object::Name("Group".into()));
dict.set("S", Object::Name("Transparency".into()));
if self.isolated {
dict.set("I", Object::Boolean(true));
}
if self.knockout {
dict.set("K", Object::Boolean(true));
}
if let Some(ref cs) = self.color_space {
dict.set("CS", Object::Name(cs.clone()));
}
dict
}
}
#[derive(Debug, Clone)]
pub(crate) struct TransparencyGroupState {
pub group: TransparencyGroup,
pub saved_state: Vec<u8>,
#[allow(dead_code)]
pub content: Vec<u8>,
}
impl TransparencyGroupState {
pub fn new(group: TransparencyGroup) -> Self {
Self {
group,
saved_state: Vec::new(),
content: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transparency_group_creation() {
let group = TransparencyGroup::new();
assert!(!group.isolated);
assert!(!group.knockout);
assert_eq!(group.opacity, 1.0);
assert!(matches!(group.blend_mode, BlendMode::Normal));
}
#[test]
fn test_isolated_group() {
let group = TransparencyGroup::isolated();
assert!(group.isolated);
assert!(!group.knockout);
}
#[test]
fn test_knockout_group() {
let group = TransparencyGroup::knockout();
assert!(!group.isolated);
assert!(group.knockout);
}
#[test]
fn test_group_builder() {
let group = TransparencyGroup::new()
.with_isolated(true)
.with_knockout(true)
.with_blend_mode(BlendMode::Multiply)
.with_opacity(0.5)
.with_color_space("DeviceRGB");
assert!(group.isolated);
assert!(group.knockout);
assert_eq!(group.opacity, 0.5);
assert!(matches!(group.blend_mode, BlendMode::Multiply));
assert_eq!(group.color_space, Some("DeviceRGB".to_string()));
}
#[test]
fn test_opacity_clamping() {
let group1 = TransparencyGroup::new().with_opacity(1.5);
assert_eq!(group1.opacity, 1.0);
let group2 = TransparencyGroup::new().with_opacity(-0.5);
assert_eq!(group2.opacity, 0.0);
}
#[test]
fn test_to_dict() {
let group = TransparencyGroup::new()
.with_isolated(true)
.with_knockout(true)
.with_color_space("DeviceCMYK");
let dict = group.to_dict();
assert_eq!(dict.get("Type"), Some(&Object::Name("Group".into())));
assert_eq!(dict.get("S"), Some(&Object::Name("Transparency".into())));
assert_eq!(dict.get("I"), Some(&Object::Boolean(true)));
assert_eq!(dict.get("K"), Some(&Object::Boolean(true)));
assert_eq!(dict.get("CS"), Some(&Object::Name("DeviceCMYK".into())));
}
#[test]
fn test_default_dict() {
let group = TransparencyGroup::new();
let dict = group.to_dict();
assert_eq!(dict.get("Type"), Some(&Object::Name("Group".into())));
assert_eq!(dict.get("S"), Some(&Object::Name("Transparency".into())));
assert!(dict.get("I").is_none());
assert!(dict.get("K").is_none());
assert!(dict.get("CS").is_none());
}
}