Skip to main content

pdfium_render/pdf/document/page/text/
search.rs

1//! Defines the [PdfPageTextSearch] struct, exposing functionality related to searching
2//! the collection of Unicode characters visible in a single [PdfPage].
3
4use crate::bindgen::{FPDF_MATCHCASE, FPDF_MATCHWHOLEWORD, FPDF_SCHHANDLE};
5use crate::pdf::document::page::text::chars::PdfPageTextCharIndex;
6use crate::pdf::document::page::text::segments::PdfPageTextSegments;
7use crate::pdf::document::page::text::PdfPageText;
8use crate::pdfium::PdfiumLibraryBindingsAccessor;
9use std::marker::PhantomData;
10use std::os::raw::c_ulong;
11
12#[cfg(doc)]
13use crate::pdf::document::page::PdfPage;
14
15/// Configures the search options that should be applied when creating a new [PdfPageTextSearch] object.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct PdfSearchOptions {
18    match_case: bool,
19    match_whole_word: bool,
20}
21
22impl PdfSearchOptions {
23    /// Creates a new [PdfSearchOptions] object with all settings initialized with their default values.
24    pub fn new() -> Self {
25        PdfSearchOptions {
26            match_case: false,
27            match_whole_word: false,
28        }
29    }
30
31    /// Controls whether the search should be limited to results that exactly match the
32    /// case of the search target. The default is `false`.
33    pub fn match_case(mut self, do_match_case: bool) -> Self {
34        self.match_case = do_match_case;
35
36        self
37    }
38
39    /// Controls whether the search should be limited to results where the search target
40    /// is a complete word, surrounded by punctuation or whitespace. The default is `false`.
41    pub fn match_whole_word(mut self, do_match_whole_word: bool) -> Self {
42        self.match_whole_word = do_match_whole_word;
43
44        self
45    }
46
47    pub(crate) fn as_pdfium(&self) -> c_ulong {
48        let mut flag = 0;
49
50        if self.match_case {
51            flag |= FPDF_MATCHCASE;
52        }
53        if self.match_whole_word {
54            flag |= FPDF_MATCHWHOLEWORD;
55        }
56
57        flag as c_ulong
58    }
59}
60
61impl Default for PdfSearchOptions {
62    #[inline]
63    fn default() -> Self {
64        PdfSearchOptions::new()
65    }
66}
67
68/// The direction in which to search for the next result.
69#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
70pub enum PdfSearchDirection {
71    SearchForward,
72    SearchBackward,
73}
74
75/// Yields the results of searching for a given string within the collection of Unicode characters
76/// visible on a single [PdfPage].
77pub struct PdfPageTextSearch<'a> {
78    search_handle: FPDF_SCHHANDLE,
79    text_page: &'a PdfPageText<'a>,
80    lifetime: PhantomData<&'a FPDF_SCHHANDLE>,
81}
82
83impl<'a> PdfPageTextSearch<'a> {
84    pub(crate) fn from_pdfium(
85        search_handle: FPDF_SCHHANDLE,
86        text_page: &'a PdfPageText<'a>,
87    ) -> Self {
88        PdfPageTextSearch {
89            search_handle,
90            text_page,
91            lifetime: PhantomData,
92        }
93    }
94
95    /// Returns the internal `FPDF_SCHHANDLE` handle for this [PdfPageTextSearch] object.
96    #[inline]
97    pub(crate) fn search_handle(&self) -> FPDF_SCHHANDLE {
98        self.search_handle
99    }
100
101    /// Returns the next search result yielded by this [PdfPageTextSearch] object
102    /// in the direction [PdfSearchDirection::SearchForward].
103    #[inline]
104    pub fn find_next(&self) -> Option<PdfPageTextSegments<'_>> {
105        self.get_next_result(PdfSearchDirection::SearchForward)
106    }
107
108    /// Returns the next search result yielded by this [PdfPageTextSearch] object
109    /// in the direction [PdfSearchDirection::SearchBackward].
110    #[inline]
111    pub fn find_previous(&self) -> Option<PdfPageTextSegments<'_>> {
112        self.get_next_result(PdfSearchDirection::SearchBackward)
113    }
114
115    /// Returns the next search result yielded by this [PdfPageTextSearch] object
116    /// in the given direction.
117    pub fn get_next_result(
118        &self,
119        direction: PdfSearchDirection,
120    ) -> Option<PdfPageTextSegments<'_>> {
121        let has_next = if direction == PdfSearchDirection::SearchForward {
122            (unsafe { self.bindings().FPDFText_FindNext(self.search_handle()) }) != 0
123        } else {
124            (unsafe { self.bindings().FPDFText_FindPrev(self.search_handle()) }) != 0
125        };
126
127        if has_next {
128            let start_index = unsafe {
129                self.bindings()
130                    .FPDFText_GetSchResultIndex(self.search_handle())
131            };
132            let count = unsafe { self.bindings().FPDFText_GetSchCount(self.search_handle()) };
133
134            Some(self.text_page.segments_subset(
135                start_index as PdfPageTextCharIndex,
136                count as PdfPageTextCharIndex,
137            ))
138        } else {
139            None
140        }
141    }
142
143    /// Returns an iterator over all search results yielded by this [PdfPageTextSearch]
144    /// object in the given direction.
145    #[inline]
146    pub fn iter(&self, direction: PdfSearchDirection) -> PdfPageTextSearchIterator<'_> {
147        PdfPageTextSearchIterator::new(self, direction)
148    }
149}
150
151impl<'a> Drop for PdfPageTextSearch<'a> {
152    /// Closes this [PdfPageTextSearch] object, releasing held memory.
153    #[inline]
154    fn drop(&mut self) {
155        unsafe {
156            self.bindings().FPDFText_FindClose(self.search_handle());
157        }
158    }
159}
160
161impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageTextSearch<'a> {}
162
163#[cfg(feature = "thread_safe")]
164unsafe impl<'a> Send for PdfPageTextSearch<'a> {}
165
166#[cfg(feature = "thread_safe")]
167unsafe impl<'a> Sync for PdfPageTextSearch<'a> {}
168
169/// An iterator over all the [PdfPageTextSegments] search results yielded by a [PdfPageTextSearch] object.
170pub struct PdfPageTextSearchIterator<'a> {
171    search: &'a PdfPageTextSearch<'a>,
172    direction: PdfSearchDirection,
173}
174
175impl<'a> PdfPageTextSearchIterator<'a> {
176    pub(crate) fn new(search: &'a PdfPageTextSearch<'a>, direction: PdfSearchDirection) -> Self {
177        PdfPageTextSearchIterator { search, direction }
178    }
179}
180
181impl<'a> Iterator for PdfPageTextSearchIterator<'a> {
182    type Item = PdfPageTextSegments<'a>;
183
184    fn next(&mut self) -> Option<Self::Item> {
185        self.search.get_next_result(self.direction)
186    }
187}