1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! A [Color Bitmap Data Table](
//! https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt) implementation.

use crate::cblc::{self, BitmapDataFormat, Metrics, MetricsFormat};
use crate::parser::{NumFrom, Stream};
use crate::{GlyphId, RasterGlyphImage, RasterImageFormat};

/// A [Color Bitmap Data Table](
/// https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt).
///
/// EBDT and bdat also share the same structure, so this is re-used for them.
#[derive(Clone, Copy)]
pub struct Table<'a> {
    locations: cblc::Table<'a>,
    data: &'a [u8],
}

impl<'a> Table<'a> {
    /// Parses a table from raw data.
    pub fn parse(locations: cblc::Table<'a>, data: &'a [u8]) -> Option<Self> {
        Some(Self { locations, data })
    }

    /// Returns a raster image for the glyph.
    pub fn get(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage<'a>> {
        let location = self.locations.get(glyph_id, pixels_per_em)?;
        let mut s = Stream::new_at(self.data, location.offset)?;
        let metrics = match location.format.metrics {
            MetricsFormat::Small => {
                let height = s.read::<u8>()?;
                let width = s.read::<u8>()?;
                let bearing_x = s.read::<i8>()?;
                let bearing_y = s.read::<i8>()?;
                s.skip::<u8>(); // advance
                Metrics {
                    x: bearing_x,
                    y: bearing_y,
                    width,
                    height,
                }
            }
            MetricsFormat::Big => {
                let height = s.read::<u8>()?;
                let width = s.read::<u8>()?;
                let hor_bearing_x = s.read::<i8>()?;
                let hor_bearing_y = s.read::<i8>()?;
                s.skip::<u8>(); // hor_advance
                s.skip::<i8>(); // ver_bearing_x
                s.skip::<i8>(); // ver_bearing_y
                s.skip::<u8>(); // ver_advance
                Metrics {
                    x: hor_bearing_x,
                    y: hor_bearing_y,
                    width,
                    height,
                }
            }
            MetricsFormat::Shared => location.metrics,
        };
        match location.format.data {
            BitmapDataFormat::ByteAligned { bit_depth } => {
                let row_len = (u32::from(metrics.width) * u32::from(bit_depth) + 7) / 8;
                let data_len = row_len * u32::from(metrics.height);
                let data = s.read_bytes(usize::num_from(data_len))?;
                Some(RasterGlyphImage {
                    x: i16::from(metrics.x),
                    // `y` in CBDT is a bottom bound, not top one.
                    y: i16::from(metrics.y) - i16::from(metrics.height),
                    width: u16::from(metrics.width),
                    height: u16::from(metrics.height),
                    pixels_per_em: location.ppem,
                    format: match bit_depth {
                        1 => RasterImageFormat::BitmapMono,
                        2 => RasterImageFormat::BitmapGray2,
                        4 => RasterImageFormat::BitmapGray4,
                        8 => RasterImageFormat::BitmapGray8,
                        32 => RasterImageFormat::BitmapPremulBgra32,
                        _ => return None,
                    },
                    data,
                })
            }
            BitmapDataFormat::BitAligned { bit_depth } => {
                let data_len = {
                    let w = u32::from(metrics.width);
                    let h = u32::from(metrics.height);
                    let d = u32::from(bit_depth);
                    (w * h * d + 7) / 8
                };

                let data = s.read_bytes(usize::num_from(data_len))?;
                Some(RasterGlyphImage {
                    x: i16::from(metrics.x),
                    // `y` in CBDT is a bottom bound, not top one.
                    y: i16::from(metrics.y) - i16::from(metrics.height),
                    width: u16::from(metrics.width),
                    height: u16::from(metrics.height),
                    pixels_per_em: location.ppem,
                    format: match bit_depth {
                        1 => RasterImageFormat::BitmapMonoPacked,
                        2 => RasterImageFormat::BitmapGray2Packed,
                        4 => RasterImageFormat::BitmapGray4Packed,
                        8 => RasterImageFormat::BitmapGray8,
                        32 => RasterImageFormat::BitmapPremulBgra32,
                        _ => return None,
                    },
                    data,
                })
            }
            BitmapDataFormat::PNG => {
                let data_len = s.read::<u32>()?;
                let data = s.read_bytes(usize::num_from(data_len))?;
                Some(RasterGlyphImage {
                    x: i16::from(metrics.x),
                    // `y` in CBDT is a bottom bound, not top one.
                    y: i16::from(metrics.y) - i16::from(metrics.height),
                    width: u16::from(metrics.width),
                    height: u16::from(metrics.height),
                    pixels_per_em: location.ppem,
                    format: RasterImageFormat::PNG,
                    data,
                })
            }
        }
    }
}

impl core::fmt::Debug for Table<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(f, "Table {{ ... }}")
    }
}