pdfium_render/pdf/font/
glyphs.rs1use crate::bindgen::FPDF_FONT;
5use crate::error::PdfiumError;
6use crate::pdf::font::glyph::PdfFontGlyph;
7use crate::pdfium::PdfiumLibraryBindingsAccessor;
8use once_cell::sync::OnceCell;
9use std::marker::PhantomData;
10use std::ops::{Range, RangeInclusive};
11use std::os::raw::c_uint;
12
13#[cfg(doc)]
14use crate::pdf::font::PdfFont;
15
16pub type PdfFontGlyphIndex = u16;
18
19pub struct PdfFontGlyphs<'a> {
21 handle: FPDF_FONT,
22 len: OnceCell<PdfFontGlyphIndex>,
23 lifetime: PhantomData<&'a FPDF_FONT>,
24}
25
26impl<'a> PdfFontGlyphs<'a> {
27 #[inline]
28 pub(crate) fn from_pdfium(handle: FPDF_FONT) -> Self {
29 Self {
30 handle,
31 len: OnceCell::new(),
32 lifetime: PhantomData,
33 }
34 }
35
36 #[inline]
42 pub(crate) fn initialize_len(&self) {
43 self.len.get_or_init(|| {
44 self.find_maximum_valid_glyph_index(u16::MIN, u16::MAX)
49 .unwrap_or(0)
50 });
51 }
52
53 fn find_maximum_valid_glyph_index(&self, min: u16, max: u16) -> Option<u16> {
55 if !(unsafe {
58 self.bindings()
59 .FPDFFont_GetGlyphPath(self.handle, max as c_uint, 1.0)
60 })
61 .is_null()
62 {
63 return Some(max);
64 }
65
66 if (unsafe {
67 self.bindings()
68 .FPDFFont_GetGlyphPath(self.handle, min as c_uint, 1.0)
69 })
70 .is_null()
71 {
72 return None;
73 }
74
75 let mid = min + (max - min) / 2;
78
79 if (unsafe {
80 self.bindings()
81 .FPDFFont_GetGlyphPath(self.handle, mid as c_uint, 1.0)
82 })
83 .is_null()
84 {
85 if mid > min {
88 self.find_maximum_valid_glyph_index(min, mid - 1)
89 } else {
90 None
91 }
92 } else {
93 if mid < max {
96 self.find_maximum_valid_glyph_index(mid + 1, max)
97 } else {
98 Some(max)
99 }
100 }
101 }
102
103 #[inline]
105 pub fn len(&self) -> PdfFontGlyphIndex {
106 self.len.get().copied().unwrap_or(0)
107 }
108
109 #[inline]
111 pub fn is_empty(&self) -> bool {
112 self.len() == 0
113 }
114
115 #[inline]
117 pub fn as_range(&self) -> Range<PdfFontGlyphIndex> {
118 0..self.len()
119 }
120
121 #[inline]
123 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfFontGlyphIndex> {
124 if self.is_empty() {
125 0..=0
126 } else {
127 0..=(self.len() - 1)
128 }
129 }
130
131 pub fn get(&self, index: PdfFontGlyphIndex) -> Result<PdfFontGlyph<'a>, PdfiumError> {
133 if index >= self.len() {
134 return Err(PdfiumError::FontGlyphIndexOutOfBounds);
135 }
136
137 Ok(PdfFontGlyph::from_pdfium(self.handle, index))
138 }
139
140 #[inline]
142 pub fn iter(&self) -> PdfFontGlyphsIterator<'_> {
143 PdfFontGlyphsIterator::new(self)
144 }
145}
146
147impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFontGlyphs<'a> {}
148
149#[cfg(feature = "thread_safe")]
150unsafe impl<'a> Send for PdfFontGlyphs<'a> {}
151
152#[cfg(feature = "thread_safe")]
153unsafe impl<'a> Sync for PdfFontGlyphs<'a> {}
154
155pub struct PdfFontGlyphsIterator<'a> {
157 glyphs: &'a PdfFontGlyphs<'a>,
158 next_index: PdfFontGlyphIndex,
159}
160
161impl<'a> PdfFontGlyphsIterator<'a> {
162 #[inline]
163 pub(crate) fn new(glyphs: &'a PdfFontGlyphs<'a>) -> Self {
164 PdfFontGlyphsIterator {
165 glyphs,
166 next_index: 0,
167 }
168 }
169}
170
171impl<'a> Iterator for PdfFontGlyphsIterator<'a> {
172 type Item = PdfFontGlyph<'a>;
173
174 fn next(&mut self) -> Option<Self::Item> {
175 let next = self.glyphs.get(self.next_index);
176
177 self.next_index += 1;
178
179 next.ok()
180 }
181}