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 std::cell::Cell;
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: Cell<Option<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: Cell::new(None),
32 lifetime: PhantomData,
33 }
34 }
35
36 #[inline]
42 pub(crate) fn initialize_len(&self) {
43 if self.len.get().is_none() {
44 let len = self
49 .find_maximum_valid_glyph_index(u16::MIN, u16::MAX)
50 .unwrap_or(0);
51
52 self.len.replace(Some(len));
53 }
54 }
55
56 fn find_maximum_valid_glyph_index(&self, min: u16, max: u16) -> Option<u16> {
58 if !(unsafe {
61 self.bindings()
62 .FPDFFont_GetGlyphPath(self.handle, max as c_uint, 1.0)
63 })
64 .is_null()
65 {
66 return Some(max);
67 }
68
69 if (unsafe {
70 self.bindings()
71 .FPDFFont_GetGlyphPath(self.handle, min as c_uint, 1.0)
72 })
73 .is_null()
74 {
75 return None;
76 }
77
78 let mid = min + (max - min) / 2;
81
82 if (unsafe {
83 self.bindings()
84 .FPDFFont_GetGlyphPath(self.handle, mid as c_uint, 1.0)
85 })
86 .is_null()
87 {
88 if mid > min {
91 self.find_maximum_valid_glyph_index(min, mid - 1)
92 } else {
93 None
94 }
95 } else {
96 if mid < max {
99 self.find_maximum_valid_glyph_index(mid + 1, max)
100 } else {
101 Some(max)
102 }
103 }
104 }
105
106 #[inline]
108 pub fn len(&self) -> PdfFontGlyphIndex {
109 self.len.get().unwrap_or(0)
110 }
111
112 #[inline]
114 pub fn is_empty(&self) -> bool {
115 self.len() == 0
116 }
117
118 #[inline]
120 pub fn as_range(&self) -> Range<PdfFontGlyphIndex> {
121 0..self.len()
122 }
123
124 #[inline]
126 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfFontGlyphIndex> {
127 if self.is_empty() {
128 0..=0
129 } else {
130 0..=(self.len() - 1)
131 }
132 }
133
134 pub fn get(&self, index: PdfFontGlyphIndex) -> Result<PdfFontGlyph<'a>, PdfiumError> {
136 if index >= self.len() {
137 return Err(PdfiumError::FontGlyphIndexOutOfBounds);
138 }
139
140 Ok(PdfFontGlyph::from_pdfium(self.handle, index))
141 }
142
143 #[inline]
145 pub fn iter(&self) -> PdfFontGlyphsIterator<'_> {
146 PdfFontGlyphsIterator::new(self)
147 }
148}
149
150impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfFontGlyphs<'a> {}
151
152#[cfg(feature = "thread_safe")]
153unsafe impl<'a> Send for PdfFontGlyphs<'a> {}
154
155#[cfg(feature = "thread_safe")]
156unsafe impl<'a> Sync for PdfFontGlyphs<'a> {}
157
158pub struct PdfFontGlyphsIterator<'a> {
160 glyphs: &'a PdfFontGlyphs<'a>,
161 next_index: PdfFontGlyphIndex,
162}
163
164impl<'a> PdfFontGlyphsIterator<'a> {
165 #[inline]
166 pub(crate) fn new(glyphs: &'a PdfFontGlyphs<'a>) -> Self {
167 PdfFontGlyphsIterator {
168 glyphs,
169 next_index: 0,
170 }
171 }
172}
173
174impl<'a> Iterator for PdfFontGlyphsIterator<'a> {
175 type Item = PdfFontGlyph<'a>;
176
177 fn next(&mut self) -> Option<Self::Item> {
178 let next = self.glyphs.get(self.next_index);
179
180 self.next_index += 1;
181
182 next.ok()
183 }
184}