Skip to main content

pdfluent_lopdf/
outlines.rs

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,
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}