pdfium_render/pdf/document/page/
links.rs1use crate::bindgen::{FPDF_DOCUMENT, FPDF_PAGE};
5use crate::error::PdfiumError;
6use crate::pdf::link::PdfLink;
7use crate::pdf::points::PdfPoints;
8use crate::pdfium::PdfiumLibraryBindingsAccessor;
9use std::marker::PhantomData;
10use std::ops::{Range, RangeInclusive};
11use std::os::raw::c_int;
12use std::ptr::null_mut;
13
14pub type PdfPageLinkIndex = usize;
16
17pub struct PdfPageLinks<'a> {
19 page_handle: FPDF_PAGE,
20 document_handle: FPDF_DOCUMENT,
21 lifetime: PhantomData<&'a FPDF_PAGE>,
22}
23
24impl<'a> PdfPageLinks<'a> {
25 #[inline]
26 pub(crate) fn from_pdfium(page_handle: FPDF_PAGE, document_handle: FPDF_DOCUMENT) -> Self {
27 PdfPageLinks {
28 page_handle,
29 document_handle,
30 lifetime: PhantomData,
31 }
32 }
33
34 #[inline]
36 pub fn len(&self) -> PdfPageLinkIndex {
37 if self.get(0).is_err() {
50 return 0;
51 }
52
53 if self.get(1).is_err() {
54 return 1;
55 }
56
57 let mut range_start = 0;
60 let mut range_end = 50;
61
62 loop {
63 if self.get(range_end).is_err() {
64 break;
65 } else {
66 range_start = range_end;
67 range_end *= 2;
68 }
69 }
70
71 loop {
74 let midpoint = range_start + (range_end - range_start) / 2;
75
76 if midpoint == range_start {
77 break;
80 }
81
82 if self.get(midpoint).is_err() {
83 range_end = midpoint;
84 } else {
85 range_start = midpoint;
86 }
87 }
88
89 range_end
90 }
91
92 #[inline]
94 pub fn is_empty(&self) -> bool {
95 self.len() == 0
96 }
97
98 #[inline]
100 pub fn as_range(&self) -> Range<PdfPageLinkIndex> {
101 0..self.len()
102 }
103
104 #[inline]
106 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageLinkIndex> {
107 if self.is_empty() {
108 0..=0
109 } else {
110 0..=(self.len() - 1)
111 }
112 }
113
114 pub fn get(&'a self, index: PdfPageLinkIndex) -> Result<PdfLink<'a>, PdfiumError> {
116 let mut start_pos = index as c_int;
117
118 let mut handle = null_mut();
119
120 if self.bindings().is_true(unsafe {
121 self.bindings()
122 .FPDFLink_Enumerate(self.page_handle, &mut start_pos, &mut handle)
123 }) && !handle.is_null()
124 {
125 Ok(PdfLink::from_pdfium(handle, self.document_handle))
126 } else {
127 Err(PdfiumError::LinkIndexOutOfBounds)
128 }
129 }
130
131 #[inline]
133 pub fn first(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
134 self.get(0)
135 .map_err(|_| PdfiumError::NoPageLinksInCollection)
136 }
137
138 #[inline]
140 pub fn last(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
141 self.get(self.len() - 1)
142 .map_err(|_| PdfiumError::NoPageLinksInCollection)
143 }
144
145 pub fn link_at_point(&self, x: PdfPoints, y: PdfPoints) -> Option<PdfLink<'_>> {
147 let handle = unsafe {
148 self.bindings().FPDFLink_GetLinkAtPoint(
149 self.page_handle,
150 x.value as f64,
151 y.value as f64,
152 )
153 };
154
155 if handle.is_null() {
156 None
157 } else {
158 Some(PdfLink::from_pdfium(handle, self.document_handle))
159 }
160 }
161
162 #[inline]
164 pub fn iter(&self) -> PdfPageLinksIterator<'_> {
165 PdfPageLinksIterator::new(self)
166 }
167}
168
169impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageLinks<'a> {}
170
171#[cfg(feature = "thread_safe")]
172unsafe impl<'a> Send for PdfPageLinks<'a> {}
173
174#[cfg(feature = "thread_safe")]
175unsafe impl<'a> Sync for PdfPageLinks<'a> {}
176
177pub struct PdfPageLinksIterator<'a> {
179 links: &'a PdfPageLinks<'a>,
180 next_index: PdfPageLinkIndex,
181}
182
183impl<'a> PdfPageLinksIterator<'a> {
184 #[inline]
185 pub(crate) fn new(links: &'a PdfPageLinks<'a>) -> Self {
186 PdfPageLinksIterator {
187 links,
188 next_index: 0,
189 }
190 }
191}
192
193impl<'a> Iterator for PdfPageLinksIterator<'a> {
194 type Item = PdfLink<'a>;
195
196 fn next(&mut self) -> Option<Self::Item> {
197 let next = self.links.get(self.next_index);
198
199 self.next_index += 1;
200
201 next.ok()
202 }
203}