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