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 | pub fn parse(locations: cblc::Table<'a>, data: &'a [u8]) -> Option<Self> { |
21 | Some(Self { locations, data }) |
22 | } |
23 | |
24 | /// Returns a raster image for the glyph. |
25 | pub fn get(&self, glyph_id: GlyphId, pixels_per_em: u16) -> Option<RasterGlyphImage<'a>> { |
26 | let location = self.locations.get(glyph_id, pixels_per_em)?; |
27 | let mut s = Stream::new_at(self.data, location.offset)?; |
28 | let metrics = match location.format.metrics { |
29 | MetricsFormat::Small => { |
30 | let height = s.read::<u8>()?; |
31 | let width = s.read::<u8>()?; |
32 | let bearing_x = s.read::<i8>()?; |
33 | let bearing_y = s.read::<i8>()?; |
34 | s.skip::<u8>(); // advance |
35 | Metrics { |
36 | x: bearing_x, |
37 | y: bearing_y, |
38 | width, |
39 | height, |
40 | } |
41 | } |
42 | MetricsFormat::Big => { |
43 | let height = s.read::<u8>()?; |
44 | let width = s.read::<u8>()?; |
45 | let hor_bearing_x = s.read::<i8>()?; |
46 | let hor_bearing_y = s.read::<i8>()?; |
47 | s.skip::<u8>(); // hor_advance |
48 | s.skip::<i8>(); // ver_bearing_x |
49 | s.skip::<i8>(); // ver_bearing_y |
50 | s.skip::<u8>(); // ver_advance |
51 | Metrics { |
52 | x: hor_bearing_x, |
53 | y: hor_bearing_y, |
54 | width, |
55 | height, |
56 | } |
57 | } |
58 | MetricsFormat::Shared => location.metrics, |
59 | }; |
60 | match location.format.data { |
61 | BitmapDataFormat::ByteAligned { bit_depth } => { |
62 | let row_len = (u32::from(metrics.width) * u32::from(bit_depth) + 7) / 8; |
63 | let data_len = row_len * u32::from(metrics.height); |
64 | let data = s.read_bytes(usize::num_from(data_len))?; |
65 | Some(RasterGlyphImage { |
66 | x: i16::from(metrics.x), |
67 | // `y` in CBDT is a bottom bound, not top one. |
68 | y: i16::from(metrics.y) - i16::from(metrics.height), |
69 | width: u16::from(metrics.width), |
70 | height: u16::from(metrics.height), |
71 | pixels_per_em: location.ppem, |
72 | format: match bit_depth { |
73 | 1 => RasterImageFormat::BitmapMono, |
74 | 2 => RasterImageFormat::BitmapGray2, |
75 | 4 => RasterImageFormat::BitmapGray4, |
76 | 8 => RasterImageFormat::BitmapGray8, |
77 | 32 => RasterImageFormat::BitmapPremulBgra32, |
78 | _ => return None, |
79 | }, |
80 | data, |
81 | }) |
82 | } |
83 | BitmapDataFormat::BitAligned { bit_depth } => { |
84 | let data_len = { |
85 | let w = u32::from(metrics.width); |
86 | let h = u32::from(metrics.height); |
87 | let d = u32::from(bit_depth); |
88 | (w * h * d + 7) / 8 |
89 | }; |
90 | |
91 | let data = s.read_bytes(usize::num_from(data_len))?; |
92 | Some(RasterGlyphImage { |
93 | x: i16::from(metrics.x), |
94 | // `y` in CBDT is a bottom bound, not top one. |
95 | y: i16::from(metrics.y) - i16::from(metrics.height), |
96 | width: u16::from(metrics.width), |
97 | height: u16::from(metrics.height), |
98 | pixels_per_em: location.ppem, |
99 | format: match bit_depth { |
100 | 1 => RasterImageFormat::BitmapMonoPacked, |
101 | 2 => RasterImageFormat::BitmapGray2Packed, |
102 | 4 => RasterImageFormat::BitmapGray4Packed, |
103 | 8 => RasterImageFormat::BitmapGray8, |
104 | 32 => RasterImageFormat::BitmapPremulBgra32, |
105 | _ => return None, |
106 | }, |
107 | data, |
108 | }) |
109 | } |
110 | BitmapDataFormat::PNG => { |
111 | let data_len = s.read::<u32>()?; |
112 | let data = s.read_bytes(usize::num_from(data_len))?; |
113 | Some(RasterGlyphImage { |
114 | x: i16::from(metrics.x), |
115 | // `y` in CBDT is a bottom bound, not top one. |
116 | y: i16::from(metrics.y) - i16::from(metrics.height), |
117 | width: u16::from(metrics.width), |
118 | height: u16::from(metrics.height), |
119 | pixels_per_em: location.ppem, |
120 | format: RasterImageFormat::PNG, |
121 | data, |
122 | }) |
123 | } |
124 | } |
125 | } |
126 | } |
127 | |
128 | impl core::fmt::Debug for Table<'_> { |
129 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
130 | write!(f, "Table {{ ... }}" ) |
131 | } |
132 | } |
133 | |