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(&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
132                .bindings()
133                .FPDFText_GetSchResultIndex(self.search_handle());
134            let count = self.bindings().FPDFText_GetSchCount(self.search_handle());
135
136            Some(self.text_page.segments_subset(
137                start_index as PdfPageTextCharIndex,
138                count as PdfPageTextCharIndex,
139            ))
140        } else {
141            None
142        }
143    }
144
145    /// Returns an iterator over all search results yielded by this [PdfPageTextSearch]
146    /// object in the given direction.
147    #[inline]
148    pub fn iter(&self, direction: PdfSearchDirection) -> PdfPageTextSearchIterator {
149        PdfPageTextSearchIterator::new(self, direction)
150    }
151}
152
153impl<'a> Drop for PdfPageTextSearch<'a> {
154    /// Closes this [PdfPageTextSearch] object, releasing held memory.
155    #[inline]
156    fn drop(&mut self) {
157        self.bindings().FPDFText_FindClose(self.search_handle());
158    }
159}
160
161/// An iterator over all the [PdfPageTextSegments] search results yielded by a [PdfPageTextSearch] object.
162pub struct PdfPageTextSearchIterator<'a> {
163    search: &'a PdfPageTextSearch<'a>,
164    direction: PdfSearchDirection,
165}
166
167impl<'a> PdfPageTextSearchIterator<'a> {
168    pub(crate) fn new(search: &'a PdfPageTextSearch<'a>, direction: PdfSearchDirection) -> Self {
169        PdfPageTextSearchIterator { search, direction }
170    }
171}
172
173impl<'a> Iterator for PdfPageTextSearchIterator<'a> {
174    type Item = PdfPageTextSegments<'a>;
175
176    fn next(&mut self) -> Option<Self::Item> {
177        self.search.get_next_result(self.direction)
178    }
179}