Coverage Report

Created: 2026-10-01 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ttf-parser/src/tables/cbdt.rs
Line
Count
Source
1
//! A [Color Bitmap Data Table](
2
//! https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt) implementation.
3
4
use crate::cblc::{self, BitmapDataFormat, Metrics, MetricsFormat};
5
use crate::parser::{NumFrom, Stream};
6
use crate::{GlyphId, RasterGlyphImage, RasterImageFormat};
7
8
/// A [Color Bitmap Data Table](
9
/// https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt).
10
///
11
/// EBDT and bdat also share the same structure, so this is re-used for them.
12
#[derive(Clone, Copy)]
13
pub struct Table<'a> {
14
    locations: cblc::Table<'a>,
15
    data: &'a [u8],
16
}
17
18
impl<'a> Table<'a> {
19
    /// Parses a table from raw data.
20
614
    pub fn parse(locations: cblc::Table<'a>, data: &'a [u8]) -> Option<Self> {
21
614
        Some(Self { locations, data })
22
614
    }
23
24
    /// Returns a raster image for the glyph.
25
587
    pub fn get(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage<'a>> {
26
587
        let location = self.locations.get(glyph_id, pixels_per_em)?;
27
226
        let mut s = Stream::new_at(self.data, location.offset)?;
28
129
        let metrics = match location.format.metrics {
29
            MetricsFormat::Small => {
30
77
                let height = s.read::<u8>()?;
31
75
                let width = s.read::<u8>()?;
32
72
                let bearing_x = s.read::<i8>()?;
33
71
                let bearing_y = s.read::<i8>()?;
34
70
                s.skip::<u8>(); // advance
35
70
                Metrics {
36
70
                    x: bearing_x,
37
70
                    y: bearing_y,
38
70
                    width,
39
70
                    height,
40
70
                }
41
            }
42
            MetricsFormat::Big => {
43
41
                let height = s.read::<u8>()?;
44
40
                let width = s.read::<u8>()?;
45
39
                let hor_bearing_x = s.read::<i8>()?;
46
38
                let hor_bearing_y = s.read::<i8>()?;
47
36
                s.skip::<u8>(); // hor_advance
48
36
                s.skip::<i8>(); // ver_bearing_x
49
36
                s.skip::<i8>(); // ver_bearing_y
50
36
                s.skip::<u8>(); // ver_advance
51
36
                Metrics {
52
36
                    x: hor_bearing_x,
53
36
                    y: hor_bearing_y,
54
36
                    width,
55
36
                    height,
56
36
                }
57
            }
58
11
            MetricsFormat::Shared => location.metrics,
59
        };
60
117
        match location.format.data {
61
28
            BitmapDataFormat::ByteAligned { bit_depth } => {
62
28
                let row_len = (u32::from(metrics.width) * u32::from(bit_depth) + 7) / 8;
63
28
                let data_len = row_len * u32::from(metrics.height);
64
28
                let data = s.read_bytes(usize::num_from(data_len))?;
65
                Some(RasterGlyphImage {
66
16
                    x: i16::from(metrics.x),
67
                    // `y` in CBDT is a bottom bound, not top one.
68
16
                    y: i16::from(metrics.y) - i16::from(metrics.height),
69
16
                    width: u16::from(metrics.width),
70
16
                    height: u16::from(metrics.height),
71
16
                    pixels_per_em: location.ppem,
72
16
                    format: match bit_depth {
73
2
                        1 => RasterImageFormat::BitmapMono,
74
1
                        2 => RasterImageFormat::BitmapGray2,
75
1
                        4 => RasterImageFormat::BitmapGray4,
76
1
                        8 => RasterImageFormat::BitmapGray8,
77
1
                        32 => RasterImageFormat::BitmapPremulBgra32,
78
10
                        _ => return None,
79
                    },
80
6
                    data,
81
                })
82
            }
83
35
            BitmapDataFormat::BitAligned { bit_depth } => {
84
35
                let data_len = {
85
35
                    let w = u32::from(metrics.width);
86
35
                    let h = u32::from(metrics.height);
87
35
                    let d = u32::from(bit_depth);
88
35
                    (w * h * d + 7) / 8
89
                };
90
91
35
                let data = s.read_bytes(usize::num_from(data_len))?;
92
                Some(RasterGlyphImage {
93
13
                    x: i16::from(metrics.x),
94
                    // `y` in CBDT is a bottom bound, not top one.
95
13
                    y: i16::from(metrics.y) - i16::from(metrics.height),
96
13
                    width: u16::from(metrics.width),
97
13
                    height: u16::from(metrics.height),
98
13
                    pixels_per_em: location.ppem,
99
13
                    format: match bit_depth {
100
3
                        1 => RasterImageFormat::BitmapMonoPacked,
101
1
                        2 => RasterImageFormat::BitmapGray2Packed,
102
1
                        4 => RasterImageFormat::BitmapGray4Packed,
103
3
                        8 => RasterImageFormat::BitmapGray8,
104
3
                        32 => RasterImageFormat::BitmapPremulBgra32,
105
2
                        _ => return None,
106
                    },
107
11
                    data,
108
                })
109
            }
110
            BitmapDataFormat::PNG => {
111
54
                let data_len = s.read::<u32>()?;
112
50
                let data = s.read_bytes(usize::num_from(data_len))?;
113
24
                Some(RasterGlyphImage {
114
24
                    x: i16::from(metrics.x),
115
24
                    // `y` in CBDT is a bottom bound, not top one.
116
24
                    y: i16::from(metrics.y) - i16::from(metrics.height),
117
24
                    width: u16::from(metrics.width),
118
24
                    height: u16::from(metrics.height),
119
24
                    pixels_per_em: location.ppem,
120
24
                    format: RasterImageFormat::PNG,
121
24
                    data,
122
24
                })
123
            }
124
        }
125
587
    }
126
}
127
128
impl core::fmt::Debug for Table<'_> {
129
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
130
0
        write!(f, "Table {{ ... }}")
131
0
    }
132
}