1use crate::error::{PdfError, Result};
4use crate::objects::{Array, Dictionary, Object, ObjectId};
5use std::collections::BTreeMap;
6
7#[derive(Debug, Clone)]
9pub struct NameTreeNode {
10 names: Option<BTreeMap<String, Object>>,
12 kids: Option<Vec<ObjectId>>,
14 limits: Option<(String, String)>,
16}
17
18impl NameTreeNode {
19 pub fn leaf(names: BTreeMap<String, Object>) -> Self {
21 let limits =
22 if let (Some(first), Some(last)) = (names.first_key_value(), names.last_key_value()) {
23 Some((first.0.clone(), last.0.clone()))
24 } else {
25 None
26 };
27
28 Self {
29 names: Some(names),
30 kids: None,
31 limits,
32 }
33 }
34
35 pub fn intermediate(kids: Vec<ObjectId>, limits: (String, String)) -> Self {
37 Self {
38 names: None,
39 kids: Some(kids),
40 limits: Some(limits),
41 }
42 }
43
44 pub fn to_dict(&self) -> Dictionary {
46 let mut dict = Dictionary::new();
47
48 if let Some(names) = &self.names {
49 let mut names_array = Array::new();
51 for (key, value) in names {
52 names_array.push(Object::String(key.clone()));
53 names_array.push(value.clone());
54 }
55 dict.set("Names", Object::Array(names_array.into()));
56 }
57
58 if let Some(kids) = &self.kids {
59 let kids_array: Array = kids.iter().map(|id| Object::Reference(*id)).collect();
61 dict.set("Kids", Object::Array(kids_array.into()));
62 }
63
64 if let Some((min, max)) = &self.limits {
65 let limits_array = Array::from(vec![
66 Object::String(min.clone()),
67 Object::String(max.clone()),
68 ]);
69 dict.set("Limits", Object::Array(limits_array.into()));
70 }
71
72 dict
73 }
74}
75
76pub struct NameTree {
78 root: NameTreeNode,
80 #[allow(dead_code)]
82 nodes: BTreeMap<ObjectId, NameTreeNode>,
83}
84
85impl Default for NameTree {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl NameTree {
92 pub fn new() -> Self {
94 Self {
95 root: NameTreeNode::leaf(BTreeMap::new()),
96 nodes: BTreeMap::new(),
97 }
98 }
99
100 pub fn add(&mut self, name: String, value: Object) {
102 if let Some(names) = &mut self.root.names {
103 names.insert(name.clone(), value);
104
105 if let Some((min, max)) = &mut self.root.limits {
107 if name < *min {
108 *min = name.clone();
109 }
110 if name > *max {
111 *max = name;
112 }
113 } else {
114 self.root.limits = Some((name.clone(), name));
115 }
116 }
117 }
118
119 pub fn get(&self, name: &str) -> Option<&Object> {
121 self.root.names.as_ref()?.get(name)
122 }
123
124 pub fn to_dict(&self) -> Dictionary {
126 self.root.to_dict()
127 }
128
129 pub fn from_dict(dict: &Dictionary) -> Result<Self> {
131 let mut tree = Self::new();
132
133 if let Some(Object::Array(names_array)) = dict.get("Names") {
134 let items: Vec<&Object> = names_array.iter().collect();
135
136 if items.len() % 2 != 0 {
137 return Err(PdfError::InvalidStructure(
138 "Names array must have even length".to_string(),
139 ));
140 }
141
142 for i in (0..items.len()).step_by(2) {
143 if let (Object::String(key), value) = (items[i], items[i + 1]) {
144 let key = key.clone();
145 tree.add(key, value.clone());
146 }
147 }
148 }
149
150 Ok(tree)
151 }
152}
153
154pub struct NamedDestinations {
156 tree: NameTree,
158}
159
160impl Default for NamedDestinations {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166impl NamedDestinations {
167 pub fn new() -> Self {
169 Self {
170 tree: NameTree::new(),
171 }
172 }
173
174 pub fn add_destination(&mut self, name: String, destination: Array) {
176 self.tree.add(name, Object::Array(destination.into()));
177 }
178
179 pub fn get_destination(&self, name: &str) -> Option<Array> {
181 match self.tree.get(name)? {
182 Object::Array(arr) => Some(Array::from(arr.clone())),
183 _ => None,
184 }
185 }
186
187 pub fn to_dict(&self) -> Dictionary {
189 self.tree.to_dict()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_name_tree_node_leaf() {
199 let mut names = BTreeMap::new();
200 names.insert("First".to_string(), Object::Integer(1));
201 names.insert("Second".to_string(), Object::Integer(2));
202
203 let node = NameTreeNode::leaf(names);
204 assert!(node.names.is_some());
205 assert!(node.kids.is_none());
206 assert_eq!(
207 node.limits,
208 Some(("First".to_string(), "Second".to_string()))
209 );
210 }
211
212 #[test]
213 fn test_name_tree_add() {
214 let mut tree = NameTree::new();
215 tree.add("Apple".to_string(), Object::Integer(1));
216 tree.add("Banana".to_string(), Object::Integer(2));
217 tree.add("Cherry".to_string(), Object::Integer(3));
218
219 assert_eq!(tree.get("Banana"), Some(&Object::Integer(2)));
220 assert_eq!(
221 tree.root.limits,
222 Some(("Apple".to_string(), "Cherry".to_string()))
223 );
224 }
225
226 #[test]
227 fn test_name_tree_to_dict() {
228 let mut tree = NameTree::new();
229 tree.add("Test".to_string(), Object::Boolean(true));
230
231 let dict = tree.to_dict();
232 assert!(dict.get("Names").is_some());
233 }
234
235 #[test]
236 fn test_named_destinations() {
237 use crate::structure::destination::{Destination, PageDestination};
238
239 let mut dests = NamedDestinations::new();
240 let dest = Destination::fit(PageDestination::PageNumber(0));
241 dests.add_destination("Home".to_string(), dest.to_array());
242
243 let retrieved = dests.get_destination("Home");
244 assert!(retrieved.is_some());
245 }
246
247 #[test]
248 fn test_name_tree_from_dict() {
249 let mut dict = Dictionary::new();
250 let names_array = Array::from(vec![
251 Object::String("First".to_string()),
252 Object::Integer(1),
253 Object::String("Second".to_string()),
254 Object::Integer(2),
255 ]);
256 dict.set("Names", Object::Array(names_array.into()));
257
258 let tree = NameTree::from_dict(&dict).unwrap();
259 assert_eq!(tree.get("First"), Some(&Object::Integer(1)));
260 assert_eq!(tree.get("Second"), Some(&Object::Integer(2)));
261 }
262
263 #[test]
264 fn test_name_tree_node_debug_clone() {
265 let mut names = BTreeMap::new();
266 names.insert("Test".to_string(), Object::Boolean(true));
267 let node = NameTreeNode::leaf(names);
268
269 let debug_str = format!("{node:?}");
270 assert!(debug_str.contains("NameTreeNode"));
271
272 let cloned = node.clone();
273 assert_eq!(cloned.limits, node.limits);
274 }
275
276 #[test]
277 fn test_name_tree_node_intermediate() {
278 let kids = vec![ObjectId::new(10, 0), ObjectId::new(20, 0)];
279 let limits = ("Alpha".to_string(), "Zeta".to_string());
280
281 let node = NameTreeNode::intermediate(kids.clone(), limits.clone());
282 assert!(node.names.is_none());
283 assert_eq!(node.kids, Some(kids));
284 assert_eq!(node.limits, Some(limits));
285 }
286
287 #[test]
288 fn test_name_tree_node_empty_leaf() {
289 let node = NameTreeNode::leaf(BTreeMap::new());
290 assert!(node.names.is_some());
291 assert!(node.limits.is_none());
292
293 let dict = node.to_dict();
294 assert!(dict.get("Names").is_some());
295 assert!(dict.get("Limits").is_none());
296 }
297
298 #[test]
299 fn test_name_tree_node_to_dict_intermediate() {
300 let kids = vec![ObjectId::new(5, 0), ObjectId::new(6, 0)];
301 let limits = ("A".to_string(), "M".to_string());
302 let node = NameTreeNode::intermediate(kids, limits);
303
304 let dict = node.to_dict();
305 assert!(dict.get("Kids").is_some());
306 assert!(dict.get("Limits").is_some());
307 assert!(dict.get("Names").is_none());
308
309 match dict.get("Kids") {
311 Some(Object::Array(arr)) => {
312 assert_eq!(arr.len(), 2);
313 assert!(matches!(arr.first(), Some(Object::Reference(_))));
314 }
315 _ => panic!("Kids should be an array"),
316 }
317
318 match dict.get("Limits") {
320 Some(Object::Array(arr)) => {
321 assert_eq!(arr.len(), 2);
322 assert_eq!(arr.first(), Some(&Object::String("A".to_string())));
323 assert_eq!(arr.get(1), Some(&Object::String("M".to_string())));
324 }
325 _ => panic!("Limits should be an array"),
326 }
327 }
328
329 #[test]
330 fn test_name_tree_default() {
331 let tree = NameTree::default();
332 assert!(tree.root.names.is_some());
333 assert_eq!(tree.get("anything"), None);
334 }
335
336 #[test]
337 fn test_name_tree_add_updates_limits() {
338 let mut tree = NameTree::new();
339
340 tree.add("Middle".to_string(), Object::Integer(1));
342 assert_eq!(
343 tree.root.limits,
344 Some(("Middle".to_string(), "Middle".to_string()))
345 );
346
347 tree.add("Beginning".to_string(), Object::Integer(2));
349 assert_eq!(
350 tree.root.limits,
351 Some(("Beginning".to_string(), "Middle".to_string()))
352 );
353
354 tree.add("Zulu".to_string(), Object::Integer(3));
356 assert_eq!(
357 tree.root.limits,
358 Some(("Beginning".to_string(), "Zulu".to_string()))
359 );
360 }
361
362 #[test]
363 fn test_name_tree_add_multiple() {
364 let mut tree = NameTree::new();
365
366 let items = vec![
368 ("Dog", Object::Integer(1)),
369 ("Apple", Object::Integer(2)),
370 ("Cat", Object::Integer(3)),
371 ("Banana", Object::Integer(4)),
372 ];
373
374 for (name, value) in items {
375 tree.add(name.to_string(), value);
376 }
377
378 assert_eq!(tree.get("Dog"), Some(&Object::Integer(1)));
380 assert_eq!(tree.get("Apple"), Some(&Object::Integer(2)));
381 assert_eq!(tree.get("Cat"), Some(&Object::Integer(3)));
382 assert_eq!(tree.get("Banana"), Some(&Object::Integer(4)));
383
384 assert_eq!(
386 tree.root.limits,
387 Some(("Apple".to_string(), "Dog".to_string()))
388 );
389 }
390
391 #[test]
392 fn test_name_tree_get_nonexistent() {
393 let mut tree = NameTree::new();
394 tree.add("Exists".to_string(), Object::Boolean(true));
395
396 assert!(tree.get("Exists").is_some());
397 assert!(tree.get("DoesNotExist").is_none());
398 }
399
400 #[test]
401 fn test_name_tree_to_dict_multiple_entries() {
402 let mut tree = NameTree::new();
403 tree.add("First".to_string(), Object::Integer(1));
404 tree.add("Second".to_string(), Object::Integer(2));
405 tree.add("Third".to_string(), Object::Integer(3));
406
407 let dict = tree.to_dict();
408
409 match dict.get("Names") {
410 Some(Object::Array(arr)) => {
411 assert_eq!(arr.len(), 6); assert_eq!(arr.first(), Some(&Object::String("First".to_string())));
414 assert_eq!(arr.get(1), Some(&Object::Integer(1)));
415 assert_eq!(arr.get(2), Some(&Object::String("Second".to_string())));
416 assert_eq!(arr.get(3), Some(&Object::Integer(2)));
417 }
418 _ => panic!("Names should be an array"),
419 }
420 }
421
422 #[test]
423 fn test_name_tree_from_dict_empty() {
424 let dict = Dictionary::new();
425 let tree = NameTree::from_dict(&dict).unwrap();
426 assert!(tree.root.names.is_some());
427 assert_eq!(tree.get("anything"), None);
428 }
429
430 #[test]
431 fn test_name_tree_from_dict_odd_length_array() {
432 let mut dict = Dictionary::new();
433 let names_array = Array::from(vec![
434 Object::String("First".to_string()),
435 Object::Integer(1),
436 Object::String("Second".to_string()),
437 ]);
439 dict.set("Names", Object::Array(names_array.into()));
440
441 let result = NameTree::from_dict(&dict);
442 assert!(result.is_err());
443 }
444
445 #[test]
446 fn test_name_tree_from_dict_non_string_keys() {
447 let mut dict = Dictionary::new();
448 let names_array = Array::from(vec![
449 Object::Integer(123), Object::Integer(1),
451 Object::String("Valid".to_string()),
452 Object::Integer(2),
453 ]);
454 dict.set("Names", Object::Array(names_array.into()));
455
456 let tree = NameTree::from_dict(&dict).unwrap();
457 assert_eq!(tree.get("Valid"), Some(&Object::Integer(2)));
459 assert!(tree.get("123").is_none());
460 }
461
462 #[test]
463 fn test_name_tree_from_dict_various_value_types() {
464 let mut dict = Dictionary::new();
465 let names_array = Array::from(vec![
466 Object::String("Bool".to_string()),
467 Object::Boolean(true),
468 Object::String("Real".to_string()),
469 Object::Real(std::f64::consts::PI),
470 Object::String("Ref".to_string()),
471 Object::Reference(ObjectId::new(5, 0)),
472 ]);
473 dict.set("Names", Object::Array(names_array.into()));
474
475 let tree = NameTree::from_dict(&dict).unwrap();
476 assert_eq!(tree.get("Bool"), Some(&Object::Boolean(true)));
477 assert_eq!(tree.get("Real"), Some(&Object::Real(std::f64::consts::PI)));
478 assert_eq!(
479 tree.get("Ref"),
480 Some(&Object::Reference(ObjectId::new(5, 0)))
481 );
482 }
483
484 #[test]
485 fn test_named_destinations_default() {
486 let dests = NamedDestinations::default();
487 assert!(dests.get_destination("anything").is_none());
488 }
489
490 #[test]
491 fn test_named_destinations_add_and_get() {
492 use crate::structure::destination::{Destination, PageDestination};
493
494 let mut dests = NamedDestinations::new();
495
496 let dest1 = Destination::fit(PageDestination::PageNumber(0));
498 let dest2 = Destination::xyz(
499 PageDestination::PageNumber(5),
500 Some(100.0),
501 Some(200.0),
502 None,
503 );
504
505 dests.add_destination("TOC".to_string(), dest1.to_array());
506 dests.add_destination("Chapter1".to_string(), dest2.to_array());
507
508 let toc = dests.get_destination("TOC");
510 assert!(toc.is_some());
511 assert!(toc.unwrap().len() >= 2);
512
513 let ch1 = dests.get_destination("Chapter1");
514 assert!(ch1.is_some());
515 assert!(ch1.unwrap().len() >= 5); assert!(dests.get_destination("NotFound").is_none());
518 }
519
520 #[test]
521 fn test_named_destinations_to_dict() {
522 use crate::structure::destination::{Destination, PageDestination};
523
524 let mut dests = NamedDestinations::new();
525 let dest = Destination::fit(PageDestination::PageNumber(10));
526 dests.add_destination("Appendix".to_string(), dest.to_array());
527
528 let dict = dests.to_dict();
529 assert!(dict.get("Names").is_some());
530 }
531
532 #[test]
533 fn test_named_destinations_get_non_array_value() {
534 let mut dests = NamedDestinations::new();
535 dests.tree.add("Invalid".to_string(), Object::Integer(123));
537
538 assert!(dests.get_destination("Invalid").is_none());
540 }
541
542 #[test]
543 fn test_name_tree_case_sensitive() {
544 let mut tree = NameTree::new();
545 tree.add("Test".to_string(), Object::Integer(1));
546 tree.add("test".to_string(), Object::Integer(2));
547 tree.add("TEST".to_string(), Object::Integer(3));
548
549 assert_eq!(tree.get("Test"), Some(&Object::Integer(1)));
550 assert_eq!(tree.get("test"), Some(&Object::Integer(2)));
551 assert_eq!(tree.get("TEST"), Some(&Object::Integer(3)));
552 }
553
554 #[test]
555 fn test_name_tree_unicode_names() {
556 let mut tree = NameTree::new();
557 tree.add("café".to_string(), Object::Integer(1));
558 tree.add("naïve".to_string(), Object::Integer(2));
559 tree.add("日本語".to_string(), Object::Integer(3));
560 tree.add("🎉".to_string(), Object::Integer(4));
561
562 assert_eq!(tree.get("café"), Some(&Object::Integer(1)));
563 assert_eq!(tree.get("naïve"), Some(&Object::Integer(2)));
564 assert_eq!(tree.get("日本語"), Some(&Object::Integer(3)));
565 assert_eq!(tree.get("🎉"), Some(&Object::Integer(4)));
566 }
567
568 #[test]
569 fn test_name_tree_empty_string_key() {
570 let mut tree = NameTree::new();
571 tree.add("".to_string(), Object::Boolean(true));
572 tree.add("Normal".to_string(), Object::Boolean(false));
573
574 assert_eq!(tree.get(""), Some(&Object::Boolean(true)));
575 assert_eq!(tree.get("Normal"), Some(&Object::Boolean(false)));
576 }
577
578 #[test]
579 fn test_name_tree_overwrite_value() {
580 let mut tree = NameTree::new();
581 tree.add("Key".to_string(), Object::Integer(1));
582 tree.add("Key".to_string(), Object::Integer(2)); assert_eq!(tree.get("Key"), Some(&Object::Integer(2)));
585 }
586
587 #[test]
588 fn test_name_tree_dictionary_values() {
589 let mut tree = NameTree::new();
590
591 let mut dict_value = Dictionary::new();
592 dict_value.set("Type", Object::Name("Test".to_string()));
593 dict_value.set("Count", Object::Integer(42));
594
595 tree.add(
596 "DictEntry".to_string(),
597 Object::Dictionary(dict_value.clone()),
598 );
599
600 match tree.get("DictEntry") {
601 Some(Object::Dictionary(d)) => {
602 assert_eq!(d.get("Type"), Some(&Object::Name("Test".to_string())));
603 assert_eq!(d.get("Count"), Some(&Object::Integer(42)));
604 }
605 _ => panic!("Should get dictionary value"),
606 }
607 }
608
609 #[test]
610 fn test_name_tree_array_values() {
611 let mut tree = NameTree::new();
612
613 let array_value = Array::from(vec![
614 Object::Integer(1),
615 Object::Integer(2),
616 Object::Integer(3),
617 ]);
618
619 tree.add("ArrayEntry".to_string(), Object::Array(array_value.into()));
620
621 match tree.get("ArrayEntry") {
622 Some(Object::Array(arr)) => {
623 assert_eq!(arr.len(), 3);
624 assert_eq!(arr.first(), Some(&Object::Integer(1)));
625 }
626 _ => panic!("Should get array value"),
627 }
628 }
629
630 #[test]
631 fn test_name_tree_btree_ordering() {
632 let mut tree = NameTree::new();
633
634 let items = ["Zebra", "Alpha", "Mike", "Charlie", "Bravo"];
636 for (i, name) in items.iter().enumerate() {
637 tree.add((*name).to_string(), Object::Integer(i as i64));
638 }
639
640 let dict = tree.to_dict();
642 match dict.get("Names") {
643 Some(Object::Array(arr)) => {
644 assert_eq!(arr.first(), Some(&Object::String("Alpha".to_string())));
646 assert_eq!(arr.get(2), Some(&Object::String("Bravo".to_string())));
647 assert_eq!(arr.get(4), Some(&Object::String("Charlie".to_string())));
648 assert_eq!(arr.get(6), Some(&Object::String("Mike".to_string())));
649 assert_eq!(arr.get(8), Some(&Object::String("Zebra".to_string())));
650 }
651 _ => panic!("Names should be an array"),
652 }
653 }
654}