use crate::bindgen::{FPDF_DOCUMENT, FPDF_PAGE};
use crate::error::PdfiumError;
use crate::pdf::link::PdfLink;
use crate::pdf::points::PdfPoints;
use crate::pdfium::PdfiumLibraryBindingsAccessor;
use std::marker::PhantomData;
use std::ops::{Range, RangeInclusive};
use std::os::raw::c_int;
use std::ptr::null_mut;
pub type PdfPageLinkIndex = usize;
pub struct PdfPageLinks<'a> {
page_handle: FPDF_PAGE,
document_handle: FPDF_DOCUMENT,
lifetime: PhantomData<&'a FPDF_PAGE>,
}
impl<'a> PdfPageLinks<'a> {
#[inline]
pub(crate) fn from_pdfium(page_handle: FPDF_PAGE, document_handle: FPDF_DOCUMENT) -> Self {
PdfPageLinks {
page_handle,
document_handle,
lifetime: PhantomData,
}
}
#[inline]
pub fn len(&self) -> PdfPageLinkIndex {
if self.get(0).is_err() {
return 0;
}
if self.get(1).is_err() {
return 1;
}
let mut range_start = 0;
let mut range_end = 50;
loop {
if self.get(range_end).is_err() {
break;
} else {
range_start = range_end;
range_end *= 2;
}
}
loop {
let midpoint = range_start + (range_end - range_start) / 2;
if midpoint == range_start {
break;
}
if self.get(midpoint).is_err() {
range_end = midpoint;
} else {
range_start = midpoint;
}
}
range_end
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn as_range(&self) -> Range<PdfPageLinkIndex> {
0..self.len()
}
#[inline]
pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageLinkIndex> {
if self.is_empty() {
0..=0
} else {
0..=(self.len() - 1)
}
}
pub fn get(&'a self, index: PdfPageLinkIndex) -> Result<PdfLink<'a>, PdfiumError> {
let mut start_pos = index as c_int;
let mut handle = null_mut();
if self.bindings().is_true(unsafe {
self.bindings()
.FPDFLink_Enumerate(self.page_handle, &mut start_pos, &mut handle)
}) && !handle.is_null()
{
Ok(PdfLink::from_pdfium(handle, self.document_handle))
} else {
Err(PdfiumError::LinkIndexOutOfBounds)
}
}
#[inline]
pub fn first(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
self.get(0)
.map_err(|_| PdfiumError::NoPageLinksInCollection)
}
#[inline]
pub fn last(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
self.get(self.len() - 1)
.map_err(|_| PdfiumError::NoPageLinksInCollection)
}
pub fn link_at_point(&self, x: PdfPoints, y: PdfPoints) -> Option<PdfLink<'_>> {
let handle = unsafe {
self.bindings().FPDFLink_GetLinkAtPoint(
self.page_handle,
x.value as f64,
y.value as f64,
)
};
if handle.is_null() {
None
} else {
Some(PdfLink::from_pdfium(handle, self.document_handle))
}
}
#[inline]
pub fn iter(&self) -> PdfPageLinksIterator<'_> {
PdfPageLinksIterator::new(self)
}
}
impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageLinks<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Send for PdfPageLinks<'a> {}
#[cfg(feature = "thread_safe")]
unsafe impl<'a> Sync for PdfPageLinks<'a> {}
pub struct PdfPageLinksIterator<'a> {
links: &'a PdfPageLinks<'a>,
next_index: PdfPageLinkIndex,
}
impl<'a> PdfPageLinksIterator<'a> {
#[inline]
pub(crate) fn new(links: &'a PdfPageLinks<'a>) -> Self {
PdfPageLinksIterator {
links,
next_index: 0,
}
}
}
impl<'a> Iterator for PdfPageLinksIterator<'a> {
type Item = PdfLink<'a>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.links.get(self.next_index);
self.next_index += 1;
next.ok()
}
}