use std::collections::HashSet;
use zpdf_core::{ObjectId, PdfObject};
use zpdf_parser::PdfFile;
#[derive(Debug, Clone, Default)]
pub struct OcConfig {
off: HashSet<ObjectId>,
on: HashSet<ObjectId>,
base_state_off: bool,
}
impl OcConfig {
pub fn group_visible(&self, id: ObjectId) -> bool {
if self.off.contains(&id) {
return false;
}
if self.on.contains(&id) {
return true;
}
!self.base_state_off
}
pub fn all_visible(&self) -> bool {
self.off.is_empty() && !self.base_state_off
}
}
pub fn parse_oc_config(file: &PdfFile) -> Option<OcConfig> {
let root_ref = file.trailer.get_ref("Root").ok()?;
let root = file.resolve(root_ref).ok()?;
let root_dict = root.as_dict().ok()?;
let ocp = resolve_dict(file, root_dict.get("OCProperties")?)?;
let d = resolve_dict(file, ocp.get("D")?).unwrap_or_default();
let mut config = OcConfig {
base_state_off: matches!(d.get_name("BaseState"), Ok("OFF")),
..Default::default()
};
for id in ref_array(file, d.get("OFF")) {
config.off.insert(id);
}
for id in ref_array(file, d.get("ON")) {
config.on.insert(id);
}
Some(config)
}
fn resolve_dict(file: &PdfFile, obj: &PdfObject) -> Option<zpdf_core::PdfDict> {
match obj {
PdfObject::Dict(d) => Some(d.clone()),
PdfObject::Ref(r) => match file.resolve(*r).ok()? {
PdfObject::Dict(d) => Some(d),
_ => None,
},
_ => None,
}
}
fn ref_array(file: &PdfFile, obj: Option<&PdfObject>) -> Vec<ObjectId> {
let arr = match obj {
Some(PdfObject::Array(a)) => a.clone(),
Some(PdfObject::Ref(r)) => match file.resolve(*r) {
Ok(PdfObject::Array(a)) => a,
_ => return Vec::new(),
},
_ => return Vec::new(),
};
arr.iter()
.filter_map(|o| match o {
PdfObject::Ref(r) => Some(*r),
_ => None,
})
.collect()
}