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::bindings::PdfiumLibraryBindings;
6use crate::pdf::document::page::text::PdfPageText;
7use crate::pdf::document::page::text::chars::PdfPageTextCharIndex;
8use crate::pdf::document::page::text::segments::PdfPageTextSegments;
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(&self, direction: PdfSearchDirection) -> Option<PdfPageTextSegments<'_>> {
124        let has_next = if direction == PdfSearchDirection::SearchForward {
125            self.bindings().FPDFText_FindNext(self.search_handle()) != 0
126        } else {
127            self.bindings().FPDFText_FindPrev(self.search_handle()) != 0
128        };
129
130        if has_next {
131            let start_index = self.bindings().FPDFText_GetSchResultIndex(self.search_handle());
132            let count = self.bindings().FPDFText_GetSchCount(self.search_handle());
133
134            Some(
135                self.text_page
136                    .segments_subset(start_index as PdfPageTextCharIndex, 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        self.bindings().FPDFText_FindClose(self.search_handle());
156    }
157}
158
159/// An iterator over all the [PdfPageTextSegments] search results yielded by a [PdfPageTextSearch] object.
160pub struct PdfPageTextSearchIterator<'a> {
161    search: &'a PdfPageTextSearch<'a>,
162    direction: PdfSearchDirection,
163}
164
165impl<'a> PdfPageTextSearchIterator<'a> {
166    pub(crate) fn new(search: &'a PdfPageTextSearch<'a>, direction: PdfSearchDirection) -> Self {
167        PdfPageTextSearchIterator { search, direction }
168    }
169}
170
171impl<'a> Iterator for PdfPageTextSearchIterator<'a> {
172    type Item = PdfPageTextSegments<'a>;
173
174    fn next(&mut self) -> Option<Self::Item> {
175        self.search.get_next_result(self.direction)
176    }
177}