use crate::document::Page;
use crate::object::{ObjRef, Object};
use crate::source::AsyncObjectSource;
use crate::tree::{resolved_dict, Entries};
#[derive(Debug, Clone, PartialEq)]
pub struct SeparationInfo {
pub pages: Vec<ObjRef>,
pub device_colorant: String,
pub color_space: Option<Vec<Object>>,
}
pub async fn separation_info_with<S: AsyncObjectSource>(
src: &S,
page: &Page,
) -> Option<SeparationInfo> {
let dict = resolved_dict(src, page.dict().get("SeparationInfo")?).await?;
let entries = Entries { src, dict: &dict };
let device_colorant = match entries.value("DeviceColorant").await? {
Object::Name(name) => name.0,
_ => entries.text("DeviceColorant").await?,
};
let pages = match entries.value("Pages").await {
Some(Object::Array(items)) => items.iter().filter_map(Object::as_ref).collect(),
_ => Vec::new(),
};
let color_space = match entries.value("ColorSpace").await {
Some(Object::Array(items)) => Some(items),
_ => None,
};
Some(SeparationInfo {
pages,
device_colorant,
color_space,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::document::Document;
use crate::object::ObjRef;
use pdfboss_testkit::PdfBuilder;
fn doc_with(page_extra: &str) -> Document {
let mut b = PdfBuilder::new();
b.object(1, "<< /Type /Catalog /Pages 2 0 R >>");
b.object(2, "<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 2 >>");
b.object(
3,
&format!("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 10 10] {page_extra} >>"),
);
b.object(4, "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 10 10] >>");
Document::load(b.build(1)).unwrap()
}
fn separation_info(page_extra: &str) -> Option<SeparationInfo> {
let doc = doc_with(page_extra);
doc.separation_info(&doc.page(0).unwrap())
}
#[test]
fn reads_the_separation_dictionary() {
let cyan = separation_info(
"/SeparationInfo << /Pages [3 0 R 4 0 R] /DeviceColorant /Cyan \
/ColorSpace [/Separation /Cyan /DeviceCMYK 5 0 R] >>",
)
.unwrap();
assert_eq!(
cyan.pages,
[ObjRef { num: 3, gen: 0 }, ObjRef { num: 4, gen: 0 }]
);
assert_eq!(cyan.device_colorant, "Cyan");
let color_space = cyan.color_space.unwrap();
assert_eq!(color_space.len(), 4);
assert_eq!(
color_space[0].as_name().map(|n| n.0.as_str()),
Some("Separation")
);
let spot =
separation_info("/SeparationInfo << /Pages [3 0 R] /DeviceColorant (PANTONE 300 C) >>")
.unwrap();
assert_eq!(spot.device_colorant, "PANTONE 300 C");
assert_eq!(spot.color_space, None);
}
#[test]
fn missing_or_malformed_separation_info_reads_as_absent() {
assert_eq!(separation_info(""), None);
assert_eq!(separation_info("/SeparationInfo 5"), None);
assert_eq!(
separation_info("/SeparationInfo << /Pages [3 0 R] >>"),
None
);
let no_pages =
separation_info("/SeparationInfo << /DeviceColorant /Cyan /Pages 7 >>").unwrap();
assert!(no_pages.pages.is_empty());
let odd =
separation_info("/SeparationInfo << /DeviceColorant /Cyan /Pages [3 0 R (x) 4 0 R] >>")
.unwrap();
assert_eq!(
odd.pages,
[ObjRef { num: 3, gen: 0 }, ObjRef { num: 4, gen: 0 }]
);
}
}