use std::hash::{Hash, Hasher};
use std::sync::Arc;
use pdf_writer::{Chunk, Obj, Ref, Str};
use tiny_skia_path::Transform;
use crate::error::KrillaResult;
use crate::geom::Point;
use crate::serialize::{PageInfo, SerializeContext};
#[derive(Hash)]
pub enum Destination {
Xyz(XyzDestination),
Named(NamedDestination),
}
impl Destination {
pub(crate) fn serialize(&self, sc: &mut SerializeContext, buffer: Obj) -> KrillaResult<()> {
match self {
Destination::Xyz(xyz) => {
let ref_ = sc.register_xyz_destination(xyz.clone());
buffer.primitive(ref_);
Ok(())
}
Destination::Named(named) => named.serialize(sc, buffer),
}
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct NamedDestination {
pub(crate) name: Arc<String>,
pub(crate) xyz_dest: Arc<XyzDestination>,
}
impl From<NamedDestination> for Destination {
fn from(val: NamedDestination) -> Self {
Destination::Named(val)
}
}
impl NamedDestination {
pub fn new(name: String, xyz_dest: XyzDestination) -> Self {
Self {
name: Arc::new(name),
xyz_dest: Arc::new(xyz_dest),
}
}
pub(crate) fn serialize(
&self,
sc: &mut SerializeContext,
destination: Obj,
) -> KrillaResult<()> {
sc.register_named_destination(self.clone());
destination.primitive(Str(self.name.as_bytes()));
Ok(())
}
}
#[derive(Debug)]
struct XyzDestRepr {
page_index: usize,
point: Point,
}
impl Hash for XyzDestRepr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.page_index.hash(state);
self.point.x.to_bits().hash(state);
self.point.y.to_bits().hash(state);
}
}
impl PartialEq for XyzDestRepr {
fn eq(&self, other: &Self) -> bool {
self.page_index == other.page_index
&& self.point.x == other.point.x
&& self.point.y == other.point.y
}
}
impl Eq for XyzDestRepr {}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct XyzDestination(Arc<XyzDestRepr>);
impl From<XyzDestination> for Destination {
fn from(val: XyzDestination) -> Self {
Destination::Xyz(val)
}
}
impl XyzDestination {
pub fn new(page_index: usize, point: Point) -> Self {
Self(Arc::new(XyzDestRepr { page_index, point }))
}
pub(crate) fn serialize(
&self,
sc: &mut SerializeContext,
root_ref: Ref,
) -> KrillaResult<Chunk> {
let mut chunk = Chunk::new();
let destination = chunk.destination(root_ref);
let page_info = sc.page_infos().get(self.0.page_index).unwrap_or_else(|| {
panic!(
"attempted to link to page {}, but document only has {} pages",
self.0.page_index + 1,
sc.page_infos().len()
)
});
let (ref_, surface_size) = match page_info {
PageInfo::Krilla {
ref_, surface_size, ..
} => (ref_, surface_size),
PageInfo::Pdf { ref_, size, .. } => (ref_, size),
};
let page_ref = *ref_;
let page_size = surface_size.height();
let mut mapped_point = self.0.point.to_tsp();
let invert_transform = Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, page_size);
invert_transform.map_point(&mut mapped_point);
destination
.page(page_ref)
.xyz(mapped_point.x, mapped_point.y, None);
Ok(chunk)
}
}