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() { 0..=0 } else { 0..=(self.len() - 1) }
117 }
118
119 pub fn get(&'a self, index: PdfPageLinkIndex) -> Result<PdfLink<'a>, PdfiumError> {
121 let mut start_pos = index as c_int;
122
123 let mut handle = null_mut();
124
125 if self.bindings.is_true(
126 self.bindings
127 .FPDFLink_Enumerate(self.page_handle, &mut start_pos, &mut handle),
128 ) && !handle.is_null()
129 {
130 Ok(PdfLink::from_pdfium(handle, self.document_handle, self.bindings))
131 } else {
132 Err(PdfiumError::LinkIndexOutOfBounds)
133 }
134 }
135
136 #[inline]
138 pub fn first(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
139 self.get(0).map_err(|_| PdfiumError::NoPageLinksInCollection)
140 }
141
142 #[inline]
144 pub fn last(&'a self) -> Result<PdfLink<'a>, PdfiumError> {
145 self.get(self.len() - 1)
146 .map_err(|_| PdfiumError::NoPageLinksInCollection)
147 }
148
149 pub fn link_at_point(&self, x: PdfPoints, y: PdfPoints) -> Option<PdfLink<'_>> {
151 let handle = self
152 .bindings
153 .FPDFLink_GetLinkAtPoint(self.page_handle, x.value as f64, y.value as f64);
154
155 if handle.is_null() {
156 None
157 } else {
158 Some(PdfLink::from_pdfium(handle, self.document_handle, self.bindings))
159 }
160 }
161
162 #[inline]
164 pub fn iter(&self) -> PdfPageLinksIterator<'_> {
165 PdfPageLinksIterator::new(self)
166 }
167}
168
169pub struct PdfPageLinksIterator<'a> {
171 links: &'a PdfPageLinks<'a>,
172 next_index: PdfPageLinkIndex,
173}
174
175impl<'a> PdfPageLinksIterator<'a> {
176 #[inline]
177 pub(crate) fn new(links: &'a PdfPageLinks<'a>) -> Self {
178 PdfPageLinksIterator { links, next_index: 0 }
179 }
180}
181
182impl<'a> Iterator for PdfPageLinksIterator<'a> {
183 type Item = PdfLink<'a>;
184
185 fn next(&mut self) -> Option<Self::Item> {
186 let next = self.links.get(self.next_index);
187
188 self.next_index += 1;
189
190 next.ok()
191 }
192}