1use indexmap::IndexMap;
2
3use super::{Destination, Dictionary, Document, Error, Object, Result};
4
5pub enum Outline {
6 Destination(Destination),
7 SubOutlines(Vec<Outline>),
8}
9
10impl Document {
11 pub fn get_outline(
12 &self, node: &Dictionary, named_destinations: &mut IndexMap<Vec<u8>, Destination>,
13 ) -> Result<Option<Outline>> {
14 let action = match self.get_dict_in_dict(node, b"A") {
15 Ok(a) => a,
16 Err(_) => {
17 return self.build_outline_result(node.get(b"Dest")?, node.get(b"Title")?, named_destinations);
18 }
19 };
20 let command = action.get(b"S")?.as_name()?;
21 if command != b"GoTo" && command != b"GoToR" {
22 return Err(Error::InvalidOutline("Expected GoTo or GoToR".to_string()));
23 }
24 let title_obj = node.get(b"Title")?;
25 let title_ref = match title_obj.as_reference() {
26 Ok(o) => o,
27 Err(_) => match title_obj.as_str() {
28 Ok(_) => return self.build_outline_result(action.get(b"D")?, title_obj, named_destinations),
29 Err(err) => return Err(err),
30 },
31 };
32 self.build_outline_result(action.get(b"D")?, self.get_object(title_ref)?, named_destinations)
33 }
34
35 pub fn get_outlines(
36 &self, mut node: Option<Object>, mut outlines: Option<Vec<Outline>>,
37 named_destinations: &mut IndexMap<Vec<u8>, Destination>,
38 ) -> Result<Option<Vec<Outline>>> {
39 if outlines.is_none() {
40 outlines = Some(Vec::new());
41 let catalog = self.catalog()?;
42 let mut dict_node = self.get_dict_in_dict(catalog, b"Outlines")?;
43 let first = self.get_dict_in_dict(dict_node, b"First");
44 if let Ok(first) = first {
45 dict_node = first;
46 }
47 let mut tree = self.get_dict_in_dict(catalog, b"Dests");
48 if tree.is_err() {
49 let names = self.get_dict_in_dict(catalog, b"Names");
50 if let Ok(names) = names {
51 let dests = self.get_dict_in_dict(names, b"Dests");
52 if dests.is_ok() {
53 tree = dests;
54 }
55 }
56 }
57 if let Ok(tree) = tree {
58 self.get_named_destinations(tree, named_destinations)?;
59 }
60 node = Some(Object::Dictionary(dict_node.clone()));
61 }
62 if node.is_none() {
63 return Ok(outlines);
64 }
65 let node = node.unwrap();
66 let mut node = match node.as_dict() {
67 Ok(n) => n,
68 Err(_) => self.get_object(node.as_reference()?)?.as_dict()?,
69 };
70 loop {
71 if let Ok(Some(outline)) = self.get_outline(node, named_destinations) {
72 if let Some(ref mut outlines) = outlines {
73 outlines.push(outline);
74 }
75 }
76 if let Ok(first) = node.get(b"First") {
77 let sub_outlines = Vec::new();
78 let sub_outlines = self.get_outlines(Some(first.clone()), Some(sub_outlines), named_destinations)?;
79 if let Some(sub_outlines) = sub_outlines {
80 if !sub_outlines.is_empty() {
81 if let Some(ref mut outlines) = outlines {
82 outlines.push(Outline::SubOutlines(sub_outlines));
83 }
84 }
85 }
86 }
87 node = match self.get_dict_in_dict(node, b"Next") {
88 Ok(n) => n,
89 Err(_) => break,
90 };
91 }
92 Ok(outlines)
93 }
94
95 fn build_outline_result(
96 &self, dest: &Object, title: &Object, named_destinations: &mut IndexMap<Vec<u8>, Destination>,
97 ) -> Result<Option<Outline>> {
98 let outline = match dest {
99 Object::Array(obj_array) => Outline::Destination(Destination::new(
100 title.to_owned(),
101 obj_array[0].clone(),
102 obj_array[1].clone(),
103 )),
104 Object::String(key, _fmt) => {
105 if let Some(destination) = named_destinations.get_mut(key) {
106 destination.set(b"Title", title.to_owned());
107 Outline::Destination(destination.clone())
108 } else {
109 return Ok(None);
110 }
111 }
112 Object::Reference(object_id) => {
113 return self.build_outline_result(self.get_object(*object_id)?, title, named_destinations);
114 }
115 _ => return Err(Error::InvalidOutline(format!("Unexpected destination {dest:?}"))),
116 };
117 Ok(Some(outline))
118 }
119}