1 //! Common bitmap (EBLC/EBDT/CBLC/CBDT) types.
2
3 include!("../../generated/generated_bitmap.rs");
4
5 impl BitmapSize {
6 /// Returns the bitmap location information for the given glyph.
7 ///
8 /// The `offset_data` parameter is provided by the `offset_data()` method
9 /// of the parent `Eblc` or `Cblc` table.
10 ///
11 /// The resulting [`BitmapLocation`] value is used by the `data()` method
12 /// in the associated `Ebdt` or `Cbdt` table to extract the bitmap data.
location( &self, offset_data: FontData, glyph_id: GlyphId, ) -> Result<BitmapLocation, ReadError>13 pub fn location(
14 &self,
15 offset_data: FontData,
16 glyph_id: GlyphId,
17 ) -> Result<BitmapLocation, ReadError> {
18 if !(self.start_glyph_index()..=self.end_glyph_index()).contains(&glyph_id) {
19 return Err(ReadError::OutOfBounds);
20 }
21 let mut location = BitmapLocation {
22 bit_depth: self.bit_depth,
23 ..BitmapLocation::default()
24 };
25 for ix in 0..self.number_of_index_subtables() {
26 let subtable = self.subtable(offset_data, ix)?;
27 if !(subtable.first_glyph_index..=subtable.last_glyph_index).contains(&glyph_id) {
28 continue;
29 }
30 // glyph index relative to the first glyph in the subtable
31 let glyph_ix =
32 glyph_id.to_u16() as usize - subtable.first_glyph_index.to_u16() as usize;
33 match &subtable.kind {
34 IndexSubtable::Format1(st) => {
35 location.format = st.image_format();
36 location.data_offset = st.image_data_offset() as usize
37 + st.sbit_offsets()
38 .get(glyph_ix)
39 .ok_or(ReadError::OutOfBounds)?
40 .get() as usize;
41 }
42 IndexSubtable::Format2(st) => {
43 location.format = st.image_format();
44 let data_size = st.image_size() as usize;
45 location.data_size = Some(data_size);
46 location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
47 location.metrics = Some(st.big_metrics()[0].clone());
48 }
49 IndexSubtable::Format3(st) => {
50 location.format = st.image_format();
51 location.data_offset = st.image_data_offset() as usize
52 + st.sbit_offsets()
53 .get(glyph_ix)
54 .ok_or(ReadError::OutOfBounds)?
55 .get() as usize;
56 }
57 IndexSubtable::Format4(st) => {
58 location.format = st.image_format();
59 let array = st.glyph_array();
60 let array_ix = match array.binary_search_by(|x| x.glyph_id().cmp(&glyph_id)) {
61 Ok(ix) => ix,
62 _ => {
63 return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32))
64 }
65 };
66 let offset1 = array[array_ix].sbit_offset() as usize;
67 let offset2 = array
68 .get(array_ix + 1)
69 .ok_or(ReadError::OutOfBounds)?
70 .sbit_offset() as usize;
71 location.data_offset = offset1;
72 location.data_size = Some(offset2 - offset1);
73 }
74 IndexSubtable::Format5(st) => {
75 location.format = st.image_format();
76 let array = st.glyph_array();
77 if array.binary_search_by(|x| x.get().cmp(&glyph_id)).is_err() {
78 return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32));
79 }
80 let data_size = st.image_size() as usize;
81 location.data_size = Some(data_size);
82 location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
83 location.metrics = Some(st.big_metrics()[0].clone());
84 }
85 }
86 return Ok(location);
87 }
88 Err(ReadError::OutOfBounds)
89 }
90
subtable<'a>( &self, offset_data: FontData<'a>, index: u32, ) -> Result<BitmapSizeSubtable<'a>, ReadError>91 fn subtable<'a>(
92 &self,
93 offset_data: FontData<'a>,
94 index: u32,
95 ) -> Result<BitmapSizeSubtable<'a>, ReadError> {
96 let base_offset = self.index_subtable_array_offset() as usize;
97 const SUBTABLE_HEADER_SIZE: usize = 8;
98 let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE;
99 let header_data = offset_data
100 .slice(header_offset..)
101 .ok_or(ReadError::OutOfBounds)?;
102 let header = IndexSubtableArray::read(header_data)?;
103 let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize;
104 let subtable_data = offset_data
105 .slice(subtable_offset..)
106 .ok_or(ReadError::OutOfBounds)?;
107 let subtable = IndexSubtable::read(subtable_data)?;
108 Ok(BitmapSizeSubtable {
109 first_glyph_index: header.first_glyph_index(),
110 last_glyph_index: header.last_glyph_index(),
111 kind: subtable,
112 })
113 }
114 }
115
116 struct BitmapSizeSubtable<'a> {
117 pub first_glyph_index: GlyphId,
118 pub last_glyph_index: GlyphId,
119 pub kind: IndexSubtable<'a>,
120 }
121
122 #[derive(Clone, Default)]
123 pub struct BitmapLocation {
124 /// Format of EBDT/CBDT image data.
125 pub format: u16,
126 /// Offset in bytes from the start of the EBDT/CBDT table.
127 pub data_offset: usize,
128 /// Size of the image data in bytes, if present in the EBLC/CBLC table.
129 pub data_size: Option<usize>,
130 /// Bit depth from the associated size. Required for computing image data
131 /// size when unspecified.
132 pub bit_depth: u8,
133 /// Full metrics, if present in the EBLC/CBLC table.
134 pub metrics: Option<BigGlyphMetrics>,
135 }
136
137 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
138 pub enum BitmapDataFormat {
139 /// The full bitmap is tightly packed according to the bit depth.
140 BitAligned,
141 /// Each row of the data is aligned to a byte boundary.
142 ByteAligned,
143 Png,
144 }
145
146 #[derive(Clone)]
147 pub enum BitmapMetrics {
148 Small(SmallGlyphMetrics),
149 Big(BigGlyphMetrics),
150 }
151
152 #[derive(Clone)]
153 pub struct BitmapData<'a> {
154 pub metrics: BitmapMetrics,
155 pub content: BitmapContent<'a>,
156 }
157
158 #[derive(Clone)]
159 pub enum BitmapContent<'a> {
160 Data(BitmapDataFormat, &'a [u8]),
161 Composite(&'a [BdtComponent]),
162 }
163
bitmap_data<'a>( offset_data: FontData<'a>, location: &BitmapLocation, is_color: bool, ) -> Result<BitmapData<'a>, ReadError>164 pub(crate) fn bitmap_data<'a>(
165 offset_data: FontData<'a>,
166 location: &BitmapLocation,
167 is_color: bool,
168 ) -> Result<BitmapData<'a>, ReadError> {
169 let mut image_data = offset_data
170 .slice(location.data_offset..)
171 .ok_or(ReadError::OutOfBounds)?
172 .cursor();
173 match location.format {
174 // Small metrics, byte-aligned data
175 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-1-small-metrics-byte-aligned-data>
176 1 => {
177 let metrics = read_small_metrics(&mut image_data)?;
178 // The data for each row is padded to a byte boundary
179 let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
180 let height = metrics.height as usize;
181 let data = image_data.read_array::<u8>(pitch * height)?;
182 Ok(BitmapData {
183 metrics: BitmapMetrics::Small(metrics),
184 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
185 })
186 }
187 // Small metrics, bit-aligned data
188 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-2-small-metrics-bit-aligned-data>
189 2 => {
190 let metrics = read_small_metrics(&mut image_data)?;
191 let width = metrics.width as usize * location.bit_depth as usize;
192 let height = metrics.height as usize;
193 // The data is tightly packed
194 let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
195 Ok(BitmapData {
196 metrics: BitmapMetrics::Small(metrics),
197 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
198 })
199 }
200 // Format 3 is obsolete
201 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-3-obsolete>
202 // Format 4 is not supported
203 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-4-not-supported-metrics-in-eblc-compressed-data>
204 // ---
205 // Metrics in EBLC/CBLC, bit-aligned image data only
206 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-5-metrics-in-eblc-bit-aligned-image-data-only>
207 5 => {
208 let metrics = location.metrics.clone().ok_or(ReadError::MalformedData(
209 "expected metrics from location table",
210 ))?;
211 let width = metrics.width as usize * location.bit_depth as usize;
212 let height = metrics.height as usize;
213 // The data is tightly packed
214 let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
215 Ok(BitmapData {
216 metrics: BitmapMetrics::Big(metrics),
217 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
218 })
219 }
220 // Big metrics, byte-aligned data
221 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-6-big-metrics-byte-aligned-data>
222 6 => {
223 let metrics = read_big_metrics(&mut image_data)?;
224 // The data for each row is padded to a byte boundary
225 let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
226 let height = metrics.height as usize;
227 let data = image_data.read_array::<u8>(pitch * height)?;
228 Ok(BitmapData {
229 metrics: BitmapMetrics::Big(metrics),
230 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
231 })
232 }
233 // Big metrics, bit-aligned data
234 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format7-big-metrics-bit-aligned-data>
235 7 => {
236 let metrics = read_big_metrics(&mut image_data)?;
237 let width = metrics.width as usize * location.bit_depth as usize;
238 let height = metrics.height as usize;
239 // The data is tightly packed
240 let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
241 Ok(BitmapData {
242 metrics: BitmapMetrics::Big(metrics),
243 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
244 })
245 }
246 // Small metrics, component data
247 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-8-small-metrics-component-data>
248 8 => {
249 let metrics = read_small_metrics(&mut image_data)?;
250 let _pad = image_data.read::<u8>()?;
251 let count = image_data.read::<u16>()? as usize;
252 let components = image_data.read_array::<BdtComponent>(count)?;
253 Ok(BitmapData {
254 metrics: BitmapMetrics::Small(metrics),
255 content: BitmapContent::Composite(components),
256 })
257 }
258 // Big metrics, component data
259 // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-9-big-metrics-component-data>
260 9 => {
261 let metrics = read_big_metrics(&mut image_data)?;
262 let count = image_data.read::<u16>()? as usize;
263 let components = image_data.read_array::<BdtComponent>(count)?;
264 Ok(BitmapData {
265 metrics: BitmapMetrics::Big(metrics),
266 content: BitmapContent::Composite(components),
267 })
268 }
269 // Small metrics, PNG image data
270 // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-17-small-metrics-png-image-data>
271 17 if is_color => {
272 let metrics = read_small_metrics(&mut image_data)?;
273 let data_len = image_data.read::<u32>()? as usize;
274 let data = image_data.read_array::<u8>(data_len)?;
275 Ok(BitmapData {
276 metrics: BitmapMetrics::Small(metrics),
277 content: BitmapContent::Data(BitmapDataFormat::Png, data),
278 })
279 }
280 // Big metrics, PNG image data
281 // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-18-big-metrics-png-image-data>
282 18 if is_color => {
283 let metrics = read_big_metrics(&mut image_data)?;
284 let data_len = image_data.read::<u32>()? as usize;
285 let data = image_data.read_array::<u8>(data_len)?;
286 Ok(BitmapData {
287 metrics: BitmapMetrics::Big(metrics),
288 content: BitmapContent::Data(BitmapDataFormat::Png, data),
289 })
290 }
291 // Metrics in CBLC table, PNG image data
292 // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-19-metrics-in-cblc-table-png-image-data>
293 19 if is_color => {
294 let metrics = location.metrics.clone().ok_or(ReadError::MalformedData(
295 "expected metrics from location table",
296 ))?;
297 let data_len = image_data.read::<u32>()? as usize;
298 let data = image_data.read_array::<u8>(data_len)?;
299 Ok(BitmapData {
300 metrics: BitmapMetrics::Big(metrics),
301 content: BitmapContent::Data(BitmapDataFormat::Png, data),
302 })
303 }
304 _ => Err(ReadError::MalformedData("unexpected bitmap data format")),
305 }
306 }
307
read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError>308 fn read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError> {
309 Ok(cursor.read_array::<SmallGlyphMetrics>(1)?[0].clone())
310 }
311
read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError>312 fn read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError> {
313 Ok(cursor.read_array::<BigGlyphMetrics>(1)?[0].clone())
314 }
315
316 #[cfg(feature = "traversal")]
317 impl SbitLineMetrics {
traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a>318 pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> {
319 FieldType::Record(self.traverse(data))
320 }
321 }
322