use std::ops::{Deref, DerefMut};
use super::{
parse_link_action_from_annot_dict, set_link_action_on_annot_dict, DestPageResolver, LinkAction,
PdfLink, SingleResolver,
};
use crate::pdf::{DocOperation, PdfDocument, PdfObject};
use crate::{Error, Matrix, Rect};
#[derive(Debug)]
pub struct PdfLinkAnnot {
inner: PdfObject,
}
impl PdfLinkAnnot {
pub(crate) fn new(obj: PdfObject) -> Self {
Self { inner: obj }
}
pub fn action(
&self,
doc: &PdfDocument,
page_no: Option<i32>,
) -> Result<Option<LinkAction>, Error> {
parse_link_action_from_annot_dict(&self.inner, doc, page_no)
}
pub fn rect(&self, page_ctm: Option<&Matrix>) -> Result<Rect, Error> {
let rect_arr = self.inner.get_dict("Rect")?.ok_or_else(|| {
Error::InvalidDestination("link annotation missing /Rect entry".into())
})?;
let x0 = rect_arr
.get_array(0)?
.ok_or_else(|| Error::InvalidDestination("/Rect[0] missing".into()))?
.as_float()?;
let y0 = rect_arr
.get_array(1)?
.ok_or_else(|| Error::InvalidDestination("/Rect[1] missing".into()))?
.as_float()?;
let x1 = rect_arr
.get_array(2)?
.ok_or_else(|| Error::InvalidDestination("/Rect[2] missing".into()))?
.as_float()?;
let y1 = rect_arr
.get_array(3)?
.ok_or_else(|| Error::InvalidDestination("/Rect[3] missing".into()))?
.as_float()?;
let pdf_rect = Rect::new(x0, y0, x1, y1);
Ok(page_ctm
.map(|ctm| pdf_rect.transform(ctm))
.unwrap_or(pdf_rect))
}
pub fn set_action(&mut self, doc: &mut PdfDocument, action: &LinkAction) -> Result<(), Error> {
let mut resolver =
SingleResolver::new(|page_obj: &PdfObject| Ok(page_obj.page_ctm()?.invert()));
self.set_action_with_resolver(doc, action, &mut resolver)
}
pub fn set_action_with_resolver(
&mut self,
doc: &mut PdfDocument,
action: &LinkAction,
resolver: &mut impl DestPageResolver,
) -> Result<(), Error> {
let operation = DocOperation::begin(doc, "Set link action")?;
let _ = self.inner.dict_delete("AA");
match action {
LinkAction::Action(_) => {
let _ = self.inner.dict_delete("Dest");
}
LinkAction::Dest(_) => {
let _ = self.inner.dict_delete("A");
}
}
set_link_action_on_annot_dict(operation.doc, &mut self.inner, action, resolver)?;
operation.commit()
}
pub fn set_rect(
&mut self,
doc: &mut PdfDocument,
rect: Rect,
annot_inv_ctm: Option<&Matrix>,
) -> Result<(), Error> {
let operation = DocOperation::begin(doc, "Set link rectangle")?;
let pdf_rect = annot_inv_ctm
.map(|inv_ctm| rect.transform(inv_ctm))
.unwrap_or(rect);
let mut rect_array = operation.doc.new_array_with_capacity(4)?;
pdf_rect.encode_into(&mut rect_array)?;
self.inner.dict_put("Rect", rect_array)?;
operation.commit()
}
pub fn to_pdf_link(
&self,
doc: &PdfDocument,
page_no: Option<i32>,
page_ctm: Option<&Matrix>,
) -> Result<Option<PdfLink>, Error> {
let Some(action) = self.action(doc, page_no)? else {
return Ok(None);
};
let bounds = self.rect(page_ctm)?;
Ok(Some(PdfLink { bounds, action }))
}
pub fn into_inner(self) -> PdfObject {
self.inner
}
}
impl Deref for PdfLinkAnnot {
type Target = PdfObject;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for PdfLinkAnnot {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}