1use crate::objects::{Array, Dictionary, Object};
4use crate::page_labels::PageLabel;
5use std::collections::BTreeMap;
6
7#[derive(Debug, Clone)]
9pub struct PageLabelTree {
10 ranges: BTreeMap<u32, PageLabel>,
12}
13
14impl PageLabelTree {
15 pub fn new() -> Self {
17 Self {
18 ranges: BTreeMap::new(),
19 }
20 }
21
22 pub fn add_range(&mut self, start_page: u32, label: PageLabel) {
24 self.ranges.insert(start_page, label);
25 }
26
27 pub fn get_label(&self, page_index: u32) -> Option<String> {
29 let mut applicable_range = None;
31 let mut range_start = 0;
32
33 for (&start, label) in &self.ranges {
34 if start <= page_index {
35 applicable_range = Some(label);
36 range_start = start;
37 } else {
38 break;
39 }
40 }
41
42 applicable_range.map(|label| {
44 let offset = page_index - range_start;
45 label.format_label(offset)
46 })
47 }
48
49 pub fn get_all_labels(&self, total_pages: u32) -> Vec<String> {
51 (0..total_pages)
52 .map(|i| self.get_label(i).unwrap_or_else(|| (i + 1).to_string()))
53 .collect()
54 }
55
56 pub fn to_dict(&self) -> Dictionary {
58 let mut dict = Dictionary::new();
59
60 let mut nums = Array::new();
62
63 for (&start_page, label) in &self.ranges {
64 nums.push(Object::Integer(start_page as i64));
65 nums.push(Object::Dictionary(label.to_dict()));
66 }
67
68 dict.set("Nums", Object::Array(nums.into()));
69
70 dict
71 }
72
73 pub fn from_dict(dict: &Dictionary) -> Option<Self> {
75 let nums_array = match dict.get("Nums")? {
76 Object::Array(arr) => arr,
77 _ => return None,
78 };
79 let mut tree = Self::new();
80
81 let elements: Vec<&Object> = nums_array.iter().collect();
83 for i in (0..elements.len()).step_by(2) {
84 if i + 1 >= elements.len() {
85 break;
86 }
87
88 let page_index = match elements[i] {
89 Object::Integer(n) => *n as u32,
90 _ => continue,
91 };
92 let label_dict = match elements[i + 1] {
93 Object::Dictionary(d) => d,
94 _ => continue,
95 };
96
97 let style = if let Some(Object::Name(type_name)) = label_dict.get("Type") {
99 match type_name.as_str() {
100 "D" => PageLabelStyle::DecimalArabic,
101 "r" => PageLabelStyle::UppercaseRoman,
102 "R" => PageLabelStyle::LowercaseRoman,
103 "A" => PageLabelStyle::UppercaseLetters,
104 "a" => PageLabelStyle::LowercaseLetters,
105 _ => PageLabelStyle::None,
106 }
107 } else {
108 PageLabelStyle::None
109 };
110
111 let mut label = PageLabel::new(style);
112
113 if let Some(Object::String(prefix)) = label_dict.get("P") {
114 label = label.with_prefix(prefix);
115 }
116
117 if let Some(Object::Integer(start)) = label_dict.get("St") {
118 label = label.starting_at(*start as u32);
119 }
120
121 tree.add_range(page_index, label);
122 }
123
124 Some(tree)
125 }
126}
127
128impl Default for PageLabelTree {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134pub struct PageLabelBuilder {
136 tree: PageLabelTree,
137 current_page: u32,
138}
139
140impl Default for PageLabelBuilder {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146impl PageLabelBuilder {
147 pub fn new() -> Self {
149 Self {
150 tree: PageLabelTree::new(),
151 current_page: 0,
152 }
153 }
154
155 pub fn add_range(mut self, num_pages: u32, label: PageLabel) -> Self {
157 self.tree.add_range(self.current_page, label);
158 self.current_page += num_pages;
159 self
160 }
161
162 pub fn decimal_pages(self, num_pages: u32) -> Self {
164 self.add_range(num_pages, PageLabel::decimal())
165 }
166
167 pub fn roman_pages(self, num_pages: u32, uppercase: bool) -> Self {
169 let label = if uppercase {
170 PageLabel::roman_uppercase()
171 } else {
172 PageLabel::roman_lowercase()
173 };
174 self.add_range(num_pages, label)
175 }
176
177 pub fn letter_pages(self, num_pages: u32, uppercase: bool) -> Self {
179 let label = if uppercase {
180 PageLabel::letters_uppercase()
181 } else {
182 PageLabel::letters_lowercase()
183 };
184 self.add_range(num_pages, label)
185 }
186
187 pub fn prefix_pages(self, num_pages: u32, prefix: impl Into<String>) -> Self {
189 self.add_range(num_pages, PageLabel::prefix_only(prefix))
190 }
191
192 pub fn build(self) -> PageLabelTree {
194 self.tree
195 }
196}
197
198use crate::page_labels::PageLabelStyle;
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_page_label_tree() {
207 let mut tree = PageLabelTree::new();
208
209 tree.add_range(0, PageLabel::roman_lowercase());
211
212 tree.add_range(3, PageLabel::decimal());
214
215 assert_eq!(tree.get_label(0), Some("i".to_string()));
217 assert_eq!(tree.get_label(1), Some("ii".to_string()));
218 assert_eq!(tree.get_label(2), Some("iii".to_string()));
219 assert_eq!(tree.get_label(3), Some("1".to_string()));
220 assert_eq!(tree.get_label(4), Some("2".to_string()));
221 assert_eq!(tree.get_label(5), Some("3".to_string()));
222 }
223
224 #[test]
225 fn test_page_label_with_prefix() {
226 let mut tree = PageLabelTree::new();
227
228 tree.add_range(0, PageLabel::prefix_only("Cover"));
230 tree.add_range(1, PageLabel::roman_lowercase().with_prefix("p. "));
231 tree.add_range(4, PageLabel::decimal().with_prefix("Chapter "));
232
233 assert_eq!(tree.get_label(0), Some("Cover".to_string()));
234 assert_eq!(tree.get_label(1), Some("p. i".to_string()));
235 assert_eq!(tree.get_label(2), Some("p. ii".to_string()));
236 assert_eq!(tree.get_label(3), Some("p. iii".to_string()));
237 assert_eq!(tree.get_label(4), Some("Chapter 1".to_string()));
238 assert_eq!(tree.get_label(5), Some("Chapter 2".to_string()));
239 }
240
241 #[test]
242 fn test_page_label_with_start() {
243 let mut tree = PageLabelTree::new();
244
245 tree.add_range(0, PageLabel::decimal().starting_at(10));
247
248 assert_eq!(tree.get_label(0), Some("10".to_string()));
249 assert_eq!(tree.get_label(1), Some("11".to_string()));
250 assert_eq!(tree.get_label(2), Some("12".to_string()));
251 }
252
253 #[test]
254 fn test_get_all_labels() {
255 let mut tree = PageLabelTree::new();
256 tree.add_range(0, PageLabel::roman_lowercase());
257 tree.add_range(2, PageLabel::decimal());
258
259 let labels = tree.get_all_labels(5);
260 assert_eq!(labels, vec!["i", "ii", "1", "2", "3"]);
261 }
262
263 #[test]
264 fn test_page_label_builder() {
265 let tree = PageLabelBuilder::new()
266 .prefix_pages(1, "Cover")
267 .roman_pages(3, false)
268 .decimal_pages(10)
269 .letter_pages(3, true)
270 .build();
271
272 assert_eq!(tree.get_label(0), Some("Cover".to_string()));
273 assert_eq!(tree.get_label(1), Some("i".to_string()));
274 assert_eq!(tree.get_label(2), Some("ii".to_string()));
275 assert_eq!(tree.get_label(3), Some("iii".to_string()));
276 assert_eq!(tree.get_label(4), Some("1".to_string()));
277 assert_eq!(tree.get_label(13), Some("10".to_string()));
278 assert_eq!(tree.get_label(14), Some("A".to_string()));
279 assert_eq!(tree.get_label(15), Some("B".to_string()));
280 assert_eq!(tree.get_label(16), Some("C".to_string()));
281 }
282
283 #[test]
284 fn test_to_dict() {
285 let mut tree = PageLabelTree::new();
286 tree.add_range(0, PageLabel::roman_lowercase());
287 tree.add_range(3, PageLabel::decimal().with_prefix("Page "));
288
289 let dict = tree.to_dict();
290 assert!(dict.get("Nums").is_some());
291 }
292}