1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use super::{Dictionary, Document, Object, ObjectId};
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct Bookmark {
    /// Children, Must be a Collection that allows for insertion of the same page ID.
    pub children: Vec<u32>,
    pub title: String,
    /// 0, 1 for italic, 2 for bold, 3 for italic bold
    pub format: u32,
    /// R,G,B
    pub color: [f32; 3],
    pub page: ObjectId,
    pub id: u32,
}

impl Bookmark {
    pub fn new(title: String, color: [f32; 3], format: u32, page: ObjectId) -> Bookmark {
        Bookmark {
            children: Vec::new(),
            title,
            format,
            color,
            page,
            id: 0,
        }
    }
}

impl Document {
    pub fn add_bookmark(&mut self, mut bookmark: Bookmark, parent: Option<u32>) -> u32 {
        self.max_bookmark_id += 1;
        let id = self.max_bookmark_id;

        bookmark.id = id;

        if let Some(p) = parent {
            if let Some(b) = self.bookmark_table.get_mut(&p) {
                b.children.push(id);
            }
        } else {
            self.bookmarks.push(id);
        }

        self.bookmark_table.insert(id, bookmark);
        id
    }

    fn outline_child(
        &self, maxid: &mut u32, parent: (ObjectId, &[u32]), processed: &mut HashMap<ObjectId, Dictionary>,
    ) -> (Option<ObjectId>, Option<ObjectId>, i64) {
        let mut first: Option<ObjectId> = None;
        let mut last: Option<ObjectId> = None;
        let count = parent.1.len();
        for i in parent.1 {
            let mut child = Dictionary::new();
            *maxid += 1;
            let id: ObjectId = (*maxid, 0);
            *maxid += 1;
            let info_id: ObjectId = (*maxid, 0);
            let bookmark = self.bookmark_table.get(i).unwrap();

            let info = dictionary! {
                "D" =>  vec![bookmark.page.into(), Object::Name("Fit".into())],
                "S" => "GoTo",
            };

            child.set("Parent", parent.0);
            child.set("Title", Object::string_literal(bookmark.title.clone()));
            child.set("A", info_id);
            child.set("F", Object::Integer(bookmark.format.into()));
            child.set(
                "C",
                vec![
                    bookmark.color[0].into(),
                    bookmark.color[1].into(),
                    bookmark.color[2].into(),
                ],
            );

            if first.is_none() {
                first = Some(id);
            } else if let Some(x) = last {
                let inner_object = processed.get_mut(&x).unwrap();
                inner_object.set("Next", id);
                child.set("Prev", x);
            }

            last = Some(id);

            if !bookmark.children.is_empty() {
                let (c_first, c_last, c_count) = self.outline_child(maxid, (id, &bookmark.children[..]), processed);

                if let Some(n) = c_first {
                    child.set("First", n);
                }

                if let Some(n) = c_last {
                    child.set("Last", n);
                }

                child.set("Count", c_count);
            }

            processed.insert(id, child);
            processed.insert(info_id, info);
        }

        (first, last, count as i64)
    }

    pub fn build_outline(&mut self) -> Option<ObjectId> {
        let mut processed: HashMap<ObjectId, Dictionary> = HashMap::new();

        if !self.bookmarks.is_empty() {
            let mut outline = Dictionary::new();
            let mut maxid = self.max_id;
            maxid += 1;
            let id: ObjectId = (maxid, 0);

            let (first, last, count) = self.outline_child(&mut maxid, (id, &self.bookmarks[..]), &mut processed);

            if let Some(n) = first {
                outline.set("First", n);
            }

            if let Some(n) = last {
                outline.set("Last", n);
            }

            outline.set("Count", Object::Integer(count));

            for (obj_id, obj) in processed.drain() {
                self.objects.insert(obj_id, obj.into());
            }

            self.objects.insert(id, outline.into());
            self.max_id = maxid;
            return Some(id);
        }

        None
    }
}