use pdf_writer::writers::OutlineItem;
use pdf_writer::{Chunk, Finish, Name, Ref, TextStr};
use crate::chunk_container::ChunkContainer;
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)
}
}
struct SerializedChildren {
first: Ref,
last: Ref,
visible_count: usize,
}
impl SerializedChildren {
fn write(&self, outlineable: &mut impl Outlineable, negate_count: bool) {
outlineable.first(self.first);
outlineable.last(self.last);
let mut count = i32::try_from(self.visible_count).unwrap();
if negate_count {
count = -count;
}
outlineable.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,
chunk_container: &mut ChunkContainer,
root: Ref,
) {
let mut chunk = sc.new_chunk();
let children = serialize_children(&self.children, root, &mut chunk, sc);
let mut outline = chunk.outline(root);
if let Some(children) = &children {
children.write(&mut outline, false);
}
outline.finish();
chunk_container.non_stream.outline = Some((root, chunk));
}
}
#[derive(Debug, Clone)]
pub struct OutlineNode {
children: Vec<OutlineNode>,
text: String,
destination: XyzDestination,
open: bool,
}
impl OutlineNode {
pub fn new(text: String, destination: XyzDestination) -> Self {
Self {
children: vec![],
text,
destination,
open: false,
}
}
pub fn with_open(mut self, open: bool) -> Self {
self.open = open;
self
}
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>,
chunk: &mut Chunk,
) -> usize {
let children = serialize_children(&self.children, root, chunk, sc);
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);
}
if let Some(children) = &children {
children.write(&mut outline_entry, !self.open);
}
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();
if self.open {
1 + children.map_or(0, |children| children.visible_count)
} else {
1
}
}
}
fn serialize_children(
children: &[OutlineNode],
parent: Ref,
chunk: &mut Chunk,
sc: &mut SerializeContext,
) -> Option<SerializedChildren> {
let mut visible_count = 0;
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();
let child_visible_count = children[i].serialize(sc, parent, last, next, prev, chunk);
visible_count += child_visible_count;
prev = cur;
cur = next;
}
Some(SerializedChildren {
first,
last,
visible_count,
})
} else {
None
}
}