1include!("../../generated/generated_bitmap.rs");
4
5impl BitmapSize {
6 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 subtable_list = self.index_subtable_list(offset_data)?;
22 let mut location = BitmapLocation {
23 bit_depth: self.bit_depth,
24 ..BitmapLocation::default()
25 };
26 for record in subtable_list.index_subtable_records() {
27 let subtable = record.index_subtable(subtable_list.offset_data())?;
28 if !(record.first_glyph_index()..=record.last_glyph_index()).contains(&glyph_id) {
29 continue;
30 }
31 let glyph_ix =
33 glyph_id.to_u32() as usize - record.first_glyph_index().to_u32() as usize;
34 match &subtable {
35 IndexSubtable::Format1(st) => {
36 location.format = st.image_format();
37 let start = st.image_data_offset() as usize
38 + st.sbit_offsets()
39 .get(glyph_ix)
40 .ok_or(ReadError::OutOfBounds)?
41 .get() as usize;
42 let end = st.image_data_offset() as usize
43 + st.sbit_offsets()
44 .get(glyph_ix + 1)
45 .ok_or(ReadError::OutOfBounds)?
46 .get() as usize;
47 location.data_offset = start;
48 if end < start {
49 return Err(ReadError::OutOfBounds);
50 }
51 location.data_size = end - start;
52 }
53 IndexSubtable::Format2(st) => {
54 location.format = st.image_format();
55 let data_size = st.image_size() as usize;
56 location.data_size = data_size;
57 location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
58 location.metrics =
59 Some(*st.big_metrics().first().ok_or(ReadError::OutOfBounds)?);
60 }
61 IndexSubtable::Format3(st) => {
62 location.format = st.image_format();
63 let start = st.image_data_offset() as usize
64 + st.sbit_offsets()
65 .get(glyph_ix)
66 .ok_or(ReadError::OutOfBounds)?
67 .get() as usize;
68 let end = st.image_data_offset() as usize
69 + st.sbit_offsets()
70 .get(glyph_ix + 1)
71 .ok_or(ReadError::OutOfBounds)?
72 .get() as usize;
73 location.data_offset = start;
74 if end < start {
75 return Err(ReadError::OutOfBounds);
76 }
77 location.data_size = end - start;
78 }
79 IndexSubtable::Format4(st) => {
80 location.format = st.image_format();
81 let array = st.glyph_array();
82 let array_ix = match array
83 .binary_search_by(|x| x.glyph_id().to_u32().cmp(&glyph_id.to_u32()))
84 {
85 Ok(ix) => ix,
86 _ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
87 };
88 let start =
89 st.image_data_offset() as usize + array[array_ix].sbit_offset() as usize;
90 let end = st.image_data_offset() as usize
91 + array
92 .get(array_ix + 1)
93 .ok_or(ReadError::OutOfBounds)?
94 .sbit_offset() as usize;
95 location.data_offset = start;
96 if end < start {
97 return Err(ReadError::OutOfBounds);
98 }
99 location.data_size = end - start;
100 }
101 IndexSubtable::Format5(st) => {
102 location.format = st.image_format();
103 let array = st.glyph_array();
104 let array_ix = match array
105 .binary_search_by(|gid| gid.get().to_u32().cmp(&glyph_id.to_u32()))
106 {
107 Ok(ix) => ix,
108 _ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
109 };
110 let data_size = st.image_size() as usize;
111 location.data_size = data_size;
112 location.data_offset = st.image_data_offset() as usize + array_ix * data_size;
113 location.metrics =
114 Some(*st.big_metrics().first().ok_or(ReadError::OutOfBounds)?);
115 }
116 }
117 return Ok(location);
118 }
119 Err(ReadError::OutOfBounds)
120 }
121
122 pub fn index_subtable_list<'a>(
127 &self,
128 offset_data: FontData<'a>,
129 ) -> Result<IndexSubtableList<'a>, ReadError> {
130 let start = self.index_subtable_list_offset() as usize;
131 let data = offset_data.split_off(start).ok_or(ReadError::OutOfBounds)?;
139 IndexSubtableList::read(data, self.number_of_index_subtables())
140 }
141}
142
143#[derive(Clone, Default)]
144pub struct BitmapLocation {
145 pub format: u16,
147 pub data_offset: usize,
149 pub data_size: usize,
151 pub bit_depth: u8,
154 pub metrics: Option<BigGlyphMetrics>,
156}
157
158impl BitmapLocation {
159 pub fn is_empty(&self) -> bool {
162 self.data_size == 0
163 }
164}
165
166#[derive(Copy, Clone, PartialEq, Eq, Debug)]
167pub enum BitmapDataFormat {
168 BitAligned,
170 ByteAligned,
172 Png,
173}
174
175#[derive(Clone)]
176pub enum BitmapMetrics {
177 Small(SmallGlyphMetrics),
178 Big(BigGlyphMetrics),
179}
180
181#[derive(Clone)]
182pub struct BitmapData<'a> {
183 pub metrics: BitmapMetrics,
184 pub content: BitmapContent<'a>,
185}
186
187#[derive(Clone)]
188pub enum BitmapContent<'a> {
189 Data(BitmapDataFormat, &'a [u8]),
190 Composite(&'a [BdtComponent]),
191}
192
193pub(crate) fn bitmap_data<'a>(
194 offset_data: FontData<'a>,
195 location: &BitmapLocation,
196 is_color: bool,
197) -> Result<BitmapData<'a>, ReadError> {
198 let mut image_data = offset_data
199 .slice(location.data_offset..location.data_offset + location.data_size)
200 .ok_or(ReadError::OutOfBounds)?
201 .cursor();
202 match location.format {
203 1 => {
206 let metrics = read_small_metrics(&mut image_data)?;
207 let pitch = (metrics.width as usize * location.bit_depth as usize).div_ceil(8);
209 let height = metrics.height as usize;
210 let data = image_data.read_array::<u8>(pitch * height)?;
211 Ok(BitmapData {
212 metrics: BitmapMetrics::Small(metrics),
213 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
214 })
215 }
216 2 => {
219 let metrics = read_small_metrics(&mut image_data)?;
220 let width = metrics.width as usize * location.bit_depth as usize;
221 let height = metrics.height as usize;
222 let data = image_data.read_array::<u8>((width * height).div_ceil(8))?;
224 Ok(BitmapData {
225 metrics: BitmapMetrics::Small(metrics),
226 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
227 })
228 }
229 5 => {
237 let metrics = location.metrics.ok_or(ReadError::MalformedData(
238 "expected metrics from location table",
239 ))?;
240 let width = metrics.width as usize * location.bit_depth as usize;
241 let height = metrics.height as usize;
242 let data = image_data.read_array::<u8>((width * height).div_ceil(8))?;
244 Ok(BitmapData {
245 metrics: BitmapMetrics::Big(metrics),
246 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
247 })
248 }
249 6 => {
252 let metrics = read_big_metrics(&mut image_data)?;
253 let pitch = (metrics.width as usize * location.bit_depth as usize).div_ceil(8);
255 let height = metrics.height as usize;
256 let data = image_data.read_array::<u8>(pitch * height)?;
257 Ok(BitmapData {
258 metrics: BitmapMetrics::Big(metrics),
259 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
260 })
261 }
262 7 => {
265 let metrics = read_big_metrics(&mut image_data)?;
266 let width = metrics.width as usize * location.bit_depth as usize;
267 let height = metrics.height as usize;
268 let data = image_data.read_array::<u8>((width * height).div_ceil(8))?;
270 Ok(BitmapData {
271 metrics: BitmapMetrics::Big(metrics),
272 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
273 })
274 }
275 8 => {
278 let metrics = read_small_metrics(&mut image_data)?;
279 let _pad = image_data.read::<u8>()?;
280 let count = image_data.read::<u16>()? as usize;
281 let components = image_data.read_array::<BdtComponent>(count)?;
282 Ok(BitmapData {
283 metrics: BitmapMetrics::Small(metrics),
284 content: BitmapContent::Composite(components),
285 })
286 }
287 9 => {
290 let metrics = read_big_metrics(&mut image_data)?;
291 let count = image_data.read::<u16>()? as usize;
292 let components = image_data.read_array::<BdtComponent>(count)?;
293 Ok(BitmapData {
294 metrics: BitmapMetrics::Big(metrics),
295 content: BitmapContent::Composite(components),
296 })
297 }
298 17 if is_color => {
301 let metrics = read_small_metrics(&mut image_data)?;
302 let data_len = image_data.read::<u32>()? as usize;
303 let data = image_data.read_array::<u8>(data_len)?;
304 Ok(BitmapData {
305 metrics: BitmapMetrics::Small(metrics),
306 content: BitmapContent::Data(BitmapDataFormat::Png, data),
307 })
308 }
309 18 if is_color => {
312 let metrics = read_big_metrics(&mut image_data)?;
313 let data_len = image_data.read::<u32>()? as usize;
314 let data = image_data.read_array::<u8>(data_len)?;
315 Ok(BitmapData {
316 metrics: BitmapMetrics::Big(metrics),
317 content: BitmapContent::Data(BitmapDataFormat::Png, data),
318 })
319 }
320 19 if is_color => {
323 let metrics = location.metrics.ok_or(ReadError::MalformedData(
324 "expected metrics from location table",
325 ))?;
326 let data_len = image_data.read::<u32>()? as usize;
327 let data = image_data.read_array::<u8>(data_len)?;
328 Ok(BitmapData {
329 metrics: BitmapMetrics::Big(metrics),
330 content: BitmapContent::Data(BitmapDataFormat::Png, data),
331 })
332 }
333 _ => Err(ReadError::MalformedData("unexpected bitmap data format")),
334 }
335}
336
337fn read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError> {
338 Ok(cursor.read_array::<SmallGlyphMetrics>(1)?[0])
339}
340
341fn read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError> {
342 Ok(cursor.read_array::<BigGlyphMetrics>(1)?[0])
343}
344
345#[cfg(feature = "experimental_traverse")]
346impl SbitLineMetrics {
347 pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> {
348 FieldType::Record(self.traverse(data))
349 }
350}
351
352#[derive(Clone)]
354pub enum IndexSubtable<'a> {
355 Format1(IndexSubtable1<'a>),
356 Format2(IndexSubtable2<'a>),
357 Format3(IndexSubtable3<'a>),
358 Format4(IndexSubtable4<'a>),
359 Format5(IndexSubtable5<'a>),
360}
361
362impl<'a> IndexSubtable<'a> {
363 pub fn offset_data(&self) -> FontData<'a> {
365 match self {
366 Self::Format1(item) => item.offset_data(),
367 Self::Format2(item) => item.offset_data(),
368 Self::Format3(item) => item.offset_data(),
369 Self::Format4(item) => item.offset_data(),
370 Self::Format5(item) => item.offset_data(),
371 }
372 }
373
374 pub fn index_format(&self) -> u16 {
376 match self {
377 Self::Format1(item) => item.index_format(),
378 Self::Format2(item) => item.index_format(),
379 Self::Format3(item) => item.index_format(),
380 Self::Format4(item) => item.index_format(),
381 Self::Format5(item) => item.index_format(),
382 }
383 }
384
385 pub fn image_format(&self) -> u16 {
387 match self {
388 Self::Format1(item) => item.image_format(),
389 Self::Format2(item) => item.image_format(),
390 Self::Format3(item) => item.image_format(),
391 Self::Format4(item) => item.image_format(),
392 Self::Format5(item) => item.image_format(),
393 }
394 }
395
396 pub fn image_data_offset(&self) -> u32 {
398 match self {
399 Self::Format1(item) => item.image_data_offset(),
400 Self::Format2(item) => item.image_data_offset(),
401 Self::Format3(item) => item.image_data_offset(),
402 Self::Format4(item) => item.image_data_offset(),
403 Self::Format5(item) => item.image_data_offset(),
404 }
405 }
406}
407
408impl ReadArgs for IndexSubtable<'_> {
409 type Args = (GlyphId16, GlyphId16);
410}
411impl<'a> FontRead<'a> for IndexSubtable<'a> {
412 fn read_with_args(data: FontData<'a>, args: Self::Args) -> Result<Self, ReadError> {
413 let format: u16 = data.read_at(0usize)?;
414 match format {
415 IndexSubtable1::FORMAT => FontRead::read_with_args(data, args).map(Self::Format1),
416 IndexSubtable2::FORMAT => FontRead::read(data).map(Self::Format2),
417 IndexSubtable3::FORMAT => FontRead::read_with_args(data, args).map(Self::Format3),
418 IndexSubtable4::FORMAT => FontRead::read(data).map(Self::Format4),
419 IndexSubtable5::FORMAT => FontRead::read(data).map(Self::Format5),
420 other => Err(ReadError::InvalidFormat(other.into())),
421 }
422 }
423}
424
425impl<'a> MinByteRange<'a> for IndexSubtable<'a> {
426 fn min_byte_range(&self) -> Range<usize> {
427 match self {
428 Self::Format1(item) => item.min_byte_range(),
429 Self::Format2(item) => item.min_byte_range(),
430 Self::Format3(item) => item.min_byte_range(),
431 Self::Format4(item) => item.min_byte_range(),
432 Self::Format5(item) => item.min_byte_range(),
433 }
434 }
435
436 fn min_table_bytes(&self) -> &'a [u8] {
437 match self {
438 Self::Format1(item) => item.min_table_bytes(),
439 Self::Format2(item) => item.min_table_bytes(),
440 Self::Format3(item) => item.min_table_bytes(),
441 Self::Format4(item) => item.min_table_bytes(),
442 Self::Format5(item) => item.min_table_bytes(),
443 }
444 }
445}
446
447#[cfg(feature = "experimental_traverse")]
448impl<'a> IndexSubtable<'a> {
449 fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
450 match self {
451 Self::Format1(table) => table,
452 Self::Format2(table) => table,
453 Self::Format3(table) => table,
454 Self::Format4(table) => table,
455 Self::Format5(table) => table,
456 }
457 }
458}
459
460#[cfg(feature = "experimental_traverse")]
461impl std::fmt::Debug for IndexSubtable<'_> {
462 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
463 self.dyn_inner().fmt(f)
464 }
465}
466
467#[cfg(feature = "experimental_traverse")]
468impl<'a> SomeTable<'a> for IndexSubtable<'a> {
469 fn type_name(&self) -> &str {
470 self.dyn_inner().type_name()
471 }
472 fn get_field(&self, idx: usize) -> Option<Field<'a>> {
473 self.dyn_inner().get_field(idx)
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::{types::GlyphId16, FontData};
481 use font_test_data::bebuffer::BeBuffer;
482
483 fn bitmap_size_location(data: &[u8], glyph_id: GlyphId) -> Result<BitmapLocation, ReadError> {
484 let data = FontData::new(data);
485 let size = data.read_ref_at::<BitmapSize>(0).unwrap();
486 size.location(data, glyph_id)
487 }
488
489 fn bitmap_size_with_subtable(subtable: &[u8]) -> BeBuffer {
490 BeBuffer::new()
491 .push(BitmapSize::RAW_BYTE_LEN as u32)
492 .push((IndexSubtableRecord::RAW_BYTE_LEN + subtable.len()) as u32)
493 .push(1u32)
494 .push(0u32)
495 .extend([0u8; SbitLineMetrics::RAW_BYTE_LEN])
496 .extend([0u8; SbitLineMetrics::RAW_BYTE_LEN])
497 .push(GlyphId16::new(0))
498 .push(GlyphId16::new(0))
499 .push(0u8)
500 .push(0u8)
501 .push(1u8)
502 .push(0u8)
503 .push(GlyphId16::new(0))
504 .push(GlyphId16::new(0))
505 .push(IndexSubtableRecord::RAW_BYTE_LEN as u32)
506 .extend(subtable.iter().copied())
507 }
508
509 #[test]
510 fn short_index_tables_size_is_ignored() {
511 let subtable = BeBuffer::new()
515 .push(2u16) .push(5u16) .push(0u32) .push(4u32) .extend([0u8; 8]); let subtable = subtable.data();
521 let data = BeBuffer::new()
522 .push(BitmapSize::RAW_BYTE_LEN as u32)
523 .push(IndexSubtableRecord::RAW_BYTE_LEN as u32)
525 .push(1u32)
526 .push(0u32)
527 .extend([0u8; SbitLineMetrics::RAW_BYTE_LEN])
528 .extend([0u8; SbitLineMetrics::RAW_BYTE_LEN])
529 .push(GlyphId16::new(0))
530 .push(GlyphId16::new(1))
531 .push(0u8)
532 .push(0u8)
533 .push(1u8)
534 .push(0u8)
535 .push(GlyphId16::new(0))
536 .push(GlyphId16::new(1))
537 .push(IndexSubtableRecord::RAW_BYTE_LEN as u32)
538 .extend(subtable.iter().copied());
539 let location = bitmap_size_location(data.data(), GlyphId::new(1)).unwrap();
540 assert_eq!(location.data_offset, 4);
541 assert_eq!(location.data_size, 4);
542 }
543
544 #[test]
545 fn format_2_truncated_metrics_returns_error_instead_of_panicking() {
546 let data =
547 bitmap_size_with_subtable(&BeBuffer::new().push(2u16).push(5u16).push(0u32).push(1u32));
548 let result = bitmap_size_location(data.data(), GlyphId::new(0));
550 assert!(matches!(result, Err(ReadError::OutOfBounds)));
551 }
552
553 #[test]
554 fn format_4_sbit_offsets_are_relative_to_image_data_offset() {
555 let data = bitmap_size_with_subtable(
556 &BeBuffer::new()
557 .push(4u16) .push(17u16) .push(1000u32) .push(1u32) .push(GlyphId16::new(0))
562 .push(20u16) .push(GlyphId16::new(1))
564 .push(30u16), );
566 let location = bitmap_size_location(data.data(), GlyphId::new(0)).unwrap();
567 assert_eq!(location.data_offset, 1020);
568 assert_eq!(location.data_size, 10);
569 }
570
571 #[test]
572 fn format_5_truncated_metrics_returns_error_instead_of_panicking() {
573 let data = bitmap_size_with_subtable(
574 &BeBuffer::new()
575 .push(5u16)
576 .push(5u16)
577 .push(0u32)
578 .push(1u32)
579 .push(0u32),
580 );
581 let result = bitmap_size_location(data.data(), GlyphId::new(0));
583 assert!(result.is_err());
584 }
585}