extern crate pdf; use std::env::args; use std::fmt; use std::collections::HashMap; use pdf::file::{FileOptions}; use pdf::object::*; use pdf::primitive::{Primitive, PdfString}; struct Indent(usize); impl fmt::Display for Indent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for _ in 0 .. self.0 { write!(f, " ")?; } Ok(()) } } fn walk_outline(r: &impl Resolve, mut node: RcRef, name_map: &impl Fn(&str) -> usize, page_map: &impl Fn(PlainRef) -> usize, depth: usize) { let indent = Indent(depth); loop { if let Some(ref title) = node.title { println!("{}title: {:?}", indent, title.to_string_lossy()); } if let Some(ref dest) = node.dest { match dest { Primitive::String(ref s) => { let name = s.to_string_lossy(); let page_nr = name_map(&name); println!("{}dest: {:?} -> page nr. {:?}", indent, name, page_nr); } Primitive::Array(ref a) => match a[0] { Primitive::Reference(r) => { let page_nr = page_map(r); println!("{}dest: {:?} -> page nr. {:?}", indent, a, page_nr); } _ => unimplemented!("invalid reference in array"), } _ => unimplemented!("invalid dest"), } } if let Some(Action::Goto(MaybeNamedDest::Direct(Dest { page: Some(page), ..}))) = node.action { let page_nr = page_map(page.get_inner()); println!("{}action -> page nr. {:?}", indent, page_nr); } if let Some(ref a) = node.se { println!("{} -> {:?}", indent, a); } if let Some(entry_ref) = node.first { let entry = r.get(entry_ref).unwrap(); walk_outline(r, entry, name_map, page_map, depth + 1); } if let Some(entry_ref) = node.next { node = r.get(entry_ref).unwrap(); continue; } break; } } fn main() { let path = args().nth(1).expect("no file given"); println!("read: {}", path); let file = FileOptions::cached().open(&path).unwrap(); let resolver = file.resolver(); let catalog = file.get_root(); let mut pages_map: HashMap = HashMap::new(); let mut count = 0; let mut dests_cb = |key: &PdfString, val: &Option| { //println!("{:?} {:?}", key, val); if let Some(Dest { page: Some(page), ..}) = val { pages_map.insert(key.to_string_lossy(), page.get_inner()); } count += 1; }; if let Some(ref names) = catalog.names { if let Some(ref dests) = names.dests { dests.walk(&resolver, &mut dests_cb).unwrap(); } } let mut pages = HashMap::new(); fn add_tree(r: &impl Resolve, pages: &mut HashMap, tree: &PageTree, current_page: &mut usize) { for &node_ref in &tree.kids { let node = r.get(node_ref).unwrap(); match *node { PagesNode::Tree(ref tree) => { add_tree(r, pages, tree, current_page); } PagesNode::Leaf(ref _page) => { pages.insert(node_ref.get_inner(), *current_page); *current_page += 1; } } } } add_tree(&resolver, &mut pages, &catalog.pages, &mut 0); let get_page_nr = |name: &str| -> usize { let page = pages_map[name]; pages[&page] }; let page_nr = |r: PlainRef| -> usize { pages[&r] }; if let Some(ref outlines) = catalog.outlines { if let Some(entry_ref) = outlines.first { let entry = resolver.get(entry_ref).unwrap(); walk_outline(&resolver, entry, &get_page_nr, &page_nr, 0); } } println!("{} items", count); }