/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 | | } |