#[derive(Clone, Debug)]
pub struct OutlineSpec {
pub title: String,
pub destination: OutlineDestination,
pub children: Vec<OutlineSpec>,
pub open: bool,
}
impl OutlineSpec {
pub fn page(title: impl Into<String>, page_index: usize) -> Self {
Self {
title: title.into(),
destination: OutlineDestination::Fit { page_index },
children: Vec::new(),
open: false,
}
}
pub fn with_child(mut self, child: OutlineSpec) -> Self {
self.children.push(child);
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum OutlineDestination {
Xyz {
page_index: usize,
left: Option<f32>,
top: Option<f32>,
zoom: Option<f32>,
},
Fit { page_index: usize },
FitH { page_index: usize, top: Option<f32> },
FitV {
page_index: usize,
left: Option<f32>,
},
FitR {
page_index: usize,
left: f32,
bottom: f32,
right: f32,
top: f32,
},
FitB { page_index: usize },
FitBH { page_index: usize, top: Option<f32> },
FitBV {
page_index: usize,
left: Option<f32>,
},
}
impl OutlineDestination {
pub fn page_index(&self) -> usize {
match *self {
Self::Xyz { page_index, .. }
| Self::Fit { page_index }
| Self::FitH { page_index, .. }
| Self::FitV { page_index, .. }
| Self::FitR { page_index, .. }
| Self::FitB { page_index }
| Self::FitBH { page_index, .. }
| Self::FitBV { page_index, .. } => page_index,
}
}
}
#[derive(Clone, Debug)]
pub struct LinkAnnotationSpec {
pub source_page_index: usize,
pub rect: [f32; 4],
pub target: LinkTarget,
}
#[derive(Clone, Debug)]
pub enum LinkTarget {
Internal(OutlineDestination),
Uri(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn outline_page_helper_builds_fit_leaf() {
let leaf = OutlineSpec::page("Chapter 1", 3);
assert_eq!(leaf.title, "Chapter 1");
assert_eq!(leaf.destination, OutlineDestination::Fit { page_index: 3 });
assert!(leaf.children.is_empty());
assert!(!leaf.open);
}
#[test]
fn outline_with_child_chains() {
let tree = OutlineSpec::page("Root", 0)
.with_child(OutlineSpec::page("Sub A", 1))
.with_child(OutlineSpec::page("Sub B", 2));
assert_eq!(tree.children.len(), 2);
assert_eq!(tree.children[0].title, "Sub A");
}
#[test]
fn destination_page_index_threads_through_every_variant() {
assert_eq!(OutlineDestination::Fit { page_index: 7 }.page_index(), 7);
assert_eq!(
OutlineDestination::Xyz {
page_index: 4,
left: Some(0.0),
top: Some(800.0),
zoom: None
}
.page_index(),
4
);
assert_eq!(
OutlineDestination::FitR {
page_index: 2,
left: 0.0,
bottom: 0.0,
right: 100.0,
top: 100.0,
}
.page_index(),
2
);
}
}