use stet_graphics::document_structure::{DestRecord, ViewSpec};
use crate::pdf_objects::PdfObj;
use crate::pdf_writer::PdfWriter;
pub fn build_dests_leaf(
writer: &mut PdfWriter,
records: &[DestRecord],
page_refs: &[u32],
) -> Option<u32> {
if records.is_empty() {
return None;
}
let mut by_name: std::collections::BTreeMap<String, &DestRecord> =
std::collections::BTreeMap::new();
for record in records {
by_name.insert(record.name.clone(), record);
}
let mut names_array: Vec<PdfObj> = Vec::with_capacity(by_name.len() * 2);
for (name, record) in &by_name {
let Some(page_ref) = page_to_ref(record.page, page_refs) else {
continue;
};
names_array.push(PdfObj::LitString(name.clone().into_bytes()));
names_array.push(page_view_dest_array(page_ref, &record.view));
}
if names_array.is_empty() {
return None;
}
let limits_low = match &names_array[0] {
PdfObj::LitString(s) => s.clone(),
_ => return None,
};
let limits_high = match &names_array[names_array.len() - 2] {
PdfObj::LitString(s) => s.clone(),
_ => return None,
};
Some(writer.add_object(&PdfObj::Dict(vec![
(
b"Limits".to_vec(),
PdfObj::Array(vec![
PdfObj::LitString(limits_low),
PdfObj::LitString(limits_high),
]),
),
(b"Names".to_vec(), PdfObj::Array(names_array)),
])))
}
pub fn write_names_root(
writer: &mut PdfWriter,
dests_leaf: Option<u32>,
embedded_files_leaf: Option<u32>,
) -> Option<u32> {
let mut entries: Vec<(Vec<u8>, PdfObj)> = Vec::new();
if let Some(r) = dests_leaf {
entries.push((b"Dests".to_vec(), PdfObj::Ref(r)));
}
if let Some(r) = embedded_files_leaf {
entries.push((b"EmbeddedFiles".to_vec(), PdfObj::Ref(r)));
}
if entries.is_empty() {
return None;
}
Some(writer.add_object(&PdfObj::Dict(entries)))
}
fn page_to_ref(page: u32, page_refs: &[u32]) -> Option<u32> {
if page == 0 {
return None;
}
page_refs.get(page as usize - 1).copied()
}
fn page_view_dest_array(page_ref: u32, view: &ViewSpec) -> PdfObj {
let mut elems: Vec<PdfObj> = vec![PdfObj::Ref(page_ref)];
match view {
ViewSpec::Xyz { left, top, zoom } => {
elems.push(PdfObj::name("XYZ"));
elems.push(opt_real(*left));
elems.push(opt_real(*top));
elems.push(opt_real(*zoom));
}
ViewSpec::Fit => elems.push(PdfObj::name("Fit")),
ViewSpec::FitH(top) => {
elems.push(PdfObj::name("FitH"));
elems.push(opt_real(*top));
}
ViewSpec::FitV(left) => {
elems.push(PdfObj::name("FitV"));
elems.push(opt_real(*left));
}
ViewSpec::FitR {
left,
bottom,
right,
top,
} => {
elems.push(PdfObj::name("FitR"));
elems.push(PdfObj::Real(*left));
elems.push(PdfObj::Real(*bottom));
elems.push(PdfObj::Real(*right));
elems.push(PdfObj::Real(*top));
}
ViewSpec::FitB => elems.push(PdfObj::name("FitB")),
ViewSpec::FitBH(top) => {
elems.push(PdfObj::name("FitBH"));
elems.push(opt_real(*top));
}
ViewSpec::FitBV(left) => {
elems.push(PdfObj::name("FitBV"));
elems.push(opt_real(*left));
}
_ => elems.push(PdfObj::name("Fit")),
}
PdfObj::Array(elems)
}
fn opt_real(v: Option<f64>) -> PdfObj {
match v {
Some(v) => PdfObj::Real(v),
None => PdfObj::Null,
}
}