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