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