use pdf_writer::writers::OutlineItem;
use pdf_writer::{Chunk, Finish, Name, Ref, TextStr};
use crate::error::KrillaResult;
use crate::interactive::destination::XyzDestination;
use crate::serialize::SerializeContext;
#[derive(Debug, Clone)]
pub struct Outline {
children: Vec<OutlineNode>,
}
impl Outline {
pub(crate) fn is_empty(&self) -> bool {
self.children.is_empty()
}
}
impl Default for Outline {
fn default() -> Self {
Self::new()
}
}
trait Outlineable {
fn first(&mut self, item: Ref) -> &mut Self;
fn last(&mut self, item: Ref) -> &mut Self;
fn count(&mut self, count: i32) -> &mut Self;
}
impl Outlineable for pdf_writer::writers::Outline<'_> {
fn first(&mut self, item: Ref) -> &mut Self {
self.first(item)
}
fn last(&mut self, item: Ref) -> &mut Self {
self.last(item)
}
fn count(&mut self, count: i32) -> &mut Self {
self.count(count)
}
}
impl Outlineable for OutlineItem<'_> {
fn first(&mut self, item: Ref) -> &mut Self {
self.first(item)
}
fn last(&mut self, item: Ref) -> &mut Self {
self.last(item)
}
fn count(&mut self, count: i32) -> &mut Self {
self.count(count)
}
}
impl Outline {
pub fn new() -> Self {
Self { children: vec![] }
}
pub fn push_child(&mut self, node: OutlineNode) {
self.children.push(node)
}
pub(crate) fn serialize(&self, sc: &mut SerializeContext, root: Ref) -> KrillaResult<Chunk> {
let mut chunk = Chunk::new();
let mut sub_chunks = vec![];
let mut outline = chunk.outline(root);
serialize_children(
&self.children,
root,
&mut sub_chunks,
sc,
&mut outline,
false,
)?;
outline.finish();
for sub_chunk in sub_chunks {
chunk.extend(&sub_chunk);
}
Ok(chunk)
}
}
#[derive(Debug, Clone)]
pub struct OutlineNode {
children: Vec<OutlineNode>,
text: String,
destination: XyzDestination,
}
impl OutlineNode {
pub fn new(text: String, destination: XyzDestination) -> Self {
Self {
children: vec![],
text,
destination,
}
}
pub fn push_child(&mut self, node: OutlineNode) {
self.children.push(node)
}
pub(crate) fn serialize(
&self,
sc: &mut SerializeContext,
parent: Ref,
root: Ref,
next: Option<Ref>,
prev: Option<Ref>,
) -> KrillaResult<Chunk> {
let mut chunk = Chunk::new();
let mut sub_chunks = vec![];
let mut outline_entry = chunk.outline_item(root);
outline_entry.parent(parent);
if let Some(next) = next {
outline_entry.next(next);
}
if let Some(prev) = prev {
outline_entry.prev(prev);
}
serialize_children(
&self.children,
root,
&mut sub_chunks,
sc,
&mut outline_entry,
true,
)?;
outline_entry.title(TextStr(&self.text));
let dest_ref = sc.register_xyz_destination(self.destination.clone());
outline_entry.pair(Name(b"Dest"), dest_ref);
outline_entry.finish();
for sub_chunk in sub_chunks {
chunk.extend(&sub_chunk);
}
Ok(chunk)
}
}
fn serialize_children(
children: &[OutlineNode],
root: Ref,
sub_chunks: &mut Vec<Chunk>,
sc: &mut SerializeContext,
outlineable: &mut impl Outlineable,
negate_count: bool,
) -> KrillaResult<()> {
if !children.is_empty() {
let first = sc.new_ref();
let mut last = first;
let mut prev = None;
let mut cur = Some(first);
for i in 0..children.len() {
let next = if i < children.len() - 1 {
Some(sc.new_ref())
} else {
None
};
last = cur.unwrap();
sub_chunks.push(children[i].serialize(sc, root, last, next, prev)?);
prev = cur;
cur = next;
}
outlineable.first(first);
outlineable.last(last);
let mut count = i32::try_from(children.len()).unwrap();
if negate_count {
count = -count;
}
outlineable.count(count);
}
Ok(())
}