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

1//! Defines the [PdfPageTextChars] struct, a collection of nominated [PdfPageTextChar]
2//! characters selected from a single [PdfPage].
3
4use crate::bindgen::{FPDF_DOCUMENT, FPDF_PAGE, FPDF_TEXTPAGE};
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::PdfiumError;
7use crate::pdf::document::page::text::char::PdfPageTextChar;
8use crate::pdf::document::page::PdfPageText;
9use crate::pdf::points::PdfPoints;
10
11#[cfg(doc)]
12use crate::pdf::document::page::PdfPage;
13
14/// The zero-based index of a single [PdfPageTextChar] inside its containing [PdfPageTextChars] collection.
15pub type PdfPageTextCharIndex = usize;
16
17/// A collection of nominated [PdfPageTextChar] characters selected from a single [PdfPage].
18pub struct PdfPageTextChars<'a> {
19    document_handle: FPDF_DOCUMENT,
20    page_handle: FPDF_PAGE,
21    text_page_handle: FPDF_TEXTPAGE,
22    char_indices: Vec<i32>,
23    bindings: &'a dyn PdfiumLibraryBindings,
24}
25
26impl<'a> PdfPageTextChars<'a> {
27    #[inline]
28    pub(crate) fn new(
29        document_handle: FPDF_DOCUMENT,
30        page_handle: FPDF_PAGE,
31        text_page_handle: FPDF_TEXTPAGE,
32        char_indices: Vec<i32>,
33        bindings: &'a dyn PdfiumLibraryBindings,
34    ) -> Self {
35        PdfPageTextChars {
36            document_handle,
37            page_handle,
38            text_page_handle,
39            char_indices,
40            bindings,
41        }
42    }
43
44    /// Returns the internal `FPDF_DOCUMENT` handle of the [PdfDocument] containing this
45    /// [PdfPageTextChars] collection.
46    #[inline]
47    pub(crate) fn document_handle(&self) -> FPDF_DOCUMENT {
48        self.document_handle
49    }
50
51    /// Returns the internal `FPDF_PAGE` handle of the [PdfPage] containing this
52    /// [PdfPageTextChars] collection.
53    #[inline]
54    pub(crate) fn page_handle(&self) -> FPDF_PAGE {
55        self.page_handle
56    }
57
58    /// Returns the internal `FPDF_TEXTPAGE` handle for this [PdfPageTextChars] collection.
59    #[inline]
60    pub(crate) fn text_page_handle(&self) -> FPDF_TEXTPAGE {
61        self.text_page_handle
62    }
63
64    /// Returns the [PdfiumLibraryBindings] used by this [PdfPageTextChars] collection.
65    #[inline]
66    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
67        self.bindings
68    }
69
70    /// Returns the index in the containing [PdfPage] of the first character in this
71    /// [PdfPageTextChars] collection, if any.
72    #[inline]
73    pub fn first_char_index(&self) -> Option<PdfPageTextCharIndex> {
74        self.char_indices
75            .first()
76            .map(|index| *index as PdfPageTextCharIndex)
77    }
78
79    /// Returns the number of individual characters in this [PdfPageTextChars] collection.
80    #[inline]
81    pub fn len(&self) -> PdfPageTextCharIndex {
82        self.char_indices.len()
83    }
84
85    /// Returns the index in the containing [PdfPage] of the last character in this
86    /// [PdfPageTextChars] collection, if any.
87    #[inline]
88    pub fn last_char_index(&self) -> Option<PdfPageTextCharIndex> {
89        self.char_indices
90            .last()
91            .map(|index| *index as PdfPageTextCharIndex)
92    }
93
94    /// Returns `true` if this [PdfPageTextChars] collection is empty.
95    #[inline]
96    pub fn is_empty(&self) -> bool {
97        self.len() == 0
98    }
99
100    /// Returns a single [PdfPageTextChar] from this [PdfPageTextChars] collection.
101    #[inline]
102    pub fn get(&self, index: PdfPageTextCharIndex) -> Result<PdfPageTextChar, PdfiumError> {
103        match self.char_indices.get(index) {
104            Some(index) => Ok(PdfPageTextChar::from_pdfium(
105                self.document_handle(),
106                self.page_handle(),
107                self.text_page_handle(),
108                *index,
109                self.bindings(),
110            )),
111            None => Err(PdfiumError::CharIndexOutOfBounds),
112        }
113    }
114
115    /// Returns the character at the given x and y positions on the containing [PdfPage], if any.
116    #[inline]
117    pub fn get_char_at_point(&self, x: PdfPoints, y: PdfPoints) -> Option<PdfPageTextChar> {
118        self.get_char_near_point(x, PdfPoints::ZERO, y, PdfPoints::ZERO)
119    }
120
121    /// Returns the character near to the given x and y positions on the containing [PdfPage],
122    /// if any. The returned character will be no further from the given positions than the given
123    /// tolerance values.
124    #[inline]
125    pub fn get_char_near_point(
126        &self,
127        x: PdfPoints,
128        tolerance_x: PdfPoints,
129        y: PdfPoints,
130        tolerance_y: PdfPoints,
131    ) -> Option<PdfPageTextChar> {
132        PdfPageText::get_char_index_near_point(
133            self.text_page_handle(),
134            x,
135            tolerance_x,
136            y,
137            tolerance_y,
138            self.bindings(),
139        )
140        .ok_or(PdfiumError::CharIndexOutOfBounds)
141        .and_then(|index| self.get(index))
142        .ok()
143    }
144
145    /// Returns an iterator over all the characters in this [PdfPageTextChars] collection.
146    #[inline]
147    pub fn iter(&self) -> PdfPageTextCharsIterator {
148        PdfPageTextCharsIterator::new(self)
149    }
150}
151
152/// An iterator over all the [PdfPageTextChar] objects in a [PdfPageTextChars] collection.
153pub struct PdfPageTextCharsIterator<'a> {
154    chars: &'a PdfPageTextChars<'a>,
155    next_index: PdfPageTextCharIndex,
156}
157
158impl<'a> PdfPageTextCharsIterator<'a> {
159    #[inline]
160    pub(crate) fn new(chars: &'a PdfPageTextChars) -> Self {
161        PdfPageTextCharsIterator {
162            chars,
163            next_index: 0,
164        }
165    }
166}
167
168impl<'a> Iterator for PdfPageTextCharsIterator<'a> {
169    type Item = PdfPageTextChar<'a>;
170
171    fn next(&mut self) -> Option<Self::Item> {
172        let next = self.chars.get(self.next_index);
173
174        self.next_index += 1;
175
176        next.ok()
177    }
178}