1 //! Common bitmap (EBLC/EBDT/CBLC/CBDT) types.
2 
3 include!("../../generated/generated_bitmap.rs");
4 
5 impl BitmapSize {
6     /// Returns the bitmap location information for the given glyph.
7     ///
8     /// The `offset_data` parameter is provided by the `offset_data()` method
9     /// of the parent `Eblc` or `Cblc` table.
10     ///
11     /// The resulting [`BitmapLocation`] value is used by the `data()` method
12     /// in the associated `Ebdt` or `Cbdt` table to extract the bitmap data.
location( &self, offset_data: FontData, glyph_id: GlyphId, ) -> Result<BitmapLocation, ReadError>13     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 mut location = BitmapLocation {
22             bit_depth: self.bit_depth,
23             ..BitmapLocation::default()
24         };
25         for ix in 0..self.number_of_index_subtables() {
26             let subtable = self.subtable(offset_data, ix)?;
27             if !(subtable.first_glyph_index..=subtable.last_glyph_index).contains(&glyph_id) {
28                 continue;
29             }
30             // glyph index relative to the first glyph in the subtable
31             let glyph_ix =
32                 glyph_id.to_u16() as usize - subtable.first_glyph_index.to_u16() as usize;
33             match &subtable.kind {
34                 IndexSubtable::Format1(st) => {
35                     location.format = st.image_format();
36                     location.data_offset = st.image_data_offset() as usize
37                         + st.sbit_offsets()
38                             .get(glyph_ix)
39                             .ok_or(ReadError::OutOfBounds)?
40                             .get() as usize;
41                 }
42                 IndexSubtable::Format2(st) => {
43                     location.format = st.image_format();
44                     let data_size = st.image_size() as usize;
45                     location.data_size = Some(data_size);
46                     location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
47                     location.metrics = Some(st.big_metrics()[0].clone());
48                 }
49                 IndexSubtable::Format3(st) => {
50                     location.format = st.image_format();
51                     location.data_offset = st.image_data_offset() as usize
52                         + st.sbit_offsets()
53                             .get(glyph_ix)
54                             .ok_or(ReadError::OutOfBounds)?
55                             .get() as usize;
56                 }
57                 IndexSubtable::Format4(st) => {
58                     location.format = st.image_format();
59                     let array = st.glyph_array();
60                     let array_ix = match array.binary_search_by(|x| x.glyph_id().cmp(&glyph_id)) {
61                         Ok(ix) => ix,
62                         _ => {
63                             return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32))
64                         }
65                     };
66                     let offset1 = array[array_ix].sbit_offset() as usize;
67                     let offset2 = array
68                         .get(array_ix + 1)
69                         .ok_or(ReadError::OutOfBounds)?
70                         .sbit_offset() as usize;
71                     location.data_offset = offset1;
72                     location.data_size = Some(offset2 - offset1);
73                 }
74                 IndexSubtable::Format5(st) => {
75                     location.format = st.image_format();
76                     let array = st.glyph_array();
77                     if array.binary_search_by(|x| x.get().cmp(&glyph_id)).is_err() {
78                         return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32));
79                     }
80                     let data_size = st.image_size() as usize;
81                     location.data_size = Some(data_size);
82                     location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
83                     location.metrics = Some(st.big_metrics()[0].clone());
84                 }
85             }
86             return Ok(location);
87         }
88         Err(ReadError::OutOfBounds)
89     }
90 
subtable<'a>( &self, offset_data: FontData<'a>, index: u32, ) -> Result<BitmapSizeSubtable<'a>, ReadError>91     fn subtable<'a>(
92         &self,
93         offset_data: FontData<'a>,
94         index: u32,
95     ) -> Result<BitmapSizeSubtable<'a>, ReadError> {
96         let base_offset = self.index_subtable_array_offset() as usize;
97         const SUBTABLE_HEADER_SIZE: usize = 8;
98         let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE;
99         let header_data = offset_data
100             .slice(header_offset..)
101             .ok_or(ReadError::OutOfBounds)?;
102         let header = IndexSubtableArray::read(header_data)?;
103         let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize;
104         let subtable_data = offset_data
105             .slice(subtable_offset..)
106             .ok_or(ReadError::OutOfBounds)?;
107         let subtable = IndexSubtable::read(subtable_data)?;
108         Ok(BitmapSizeSubtable {
109             first_glyph_index: header.first_glyph_index(),
110             last_glyph_index: header.last_glyph_index(),
111             kind: subtable,
112         })
113     }
114 }
115 
116 struct BitmapSizeSubtable<'a> {
117     pub first_glyph_index: GlyphId,
118     pub last_glyph_index: GlyphId,
119     pub kind: IndexSubtable<'a>,
120 }
121 
122 #[derive(Clone, Default)]
123 pub struct BitmapLocation {
124     /// Format of EBDT/CBDT image data.
125     pub format: u16,
126     /// Offset in bytes from the start of the EBDT/CBDT table.
127     pub data_offset: usize,
128     /// Size of the image data in bytes, if present in the EBLC/CBLC table.
129     pub data_size: Option<usize>,
130     /// Bit depth from the associated size. Required for computing image data
131     /// size when unspecified.
132     pub bit_depth: u8,
133     /// Full metrics, if present in the EBLC/CBLC table.
134     pub metrics: Option<BigGlyphMetrics>,
135 }
136 
137 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
138 pub enum BitmapDataFormat {
139     /// The full bitmap is tightly packed according to the bit depth.
140     BitAligned,
141     /// Each row of the data is aligned to a byte boundary.
142     ByteAligned,
143     Png,
144 }
145 
146 #[derive(Clone)]
147 pub enum BitmapMetrics {
148     Small(SmallGlyphMetrics),
149     Big(BigGlyphMetrics),
150 }
151 
152 #[derive(Clone)]
153 pub struct BitmapData<'a> {
154     pub metrics: BitmapMetrics,
155     pub content: BitmapContent<'a>,
156 }
157 
158 #[derive(Clone)]
159 pub enum BitmapContent<'a> {
160     Data(BitmapDataFormat, &'a [u8]),
161     Composite(&'a [BdtComponent]),
162 }
163 
bitmap_data<'a>( offset_data: FontData<'a>, location: &BitmapLocation, is_color: bool, ) -> Result<BitmapData<'a>, ReadError>164 pub(crate) fn bitmap_data<'a>(
165     offset_data: FontData<'a>,
166     location: &BitmapLocation,
167     is_color: bool,
168 ) -> Result<BitmapData<'a>, ReadError> {
169     let mut image_data = offset_data
170         .slice(location.data_offset..)
171         .ok_or(ReadError::OutOfBounds)?
172         .cursor();
173     match location.format {
174         // Small metrics, byte-aligned data
175         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-1-small-metrics-byte-aligned-data>
176         1 => {
177             let metrics = read_small_metrics(&mut image_data)?;
178             // The data for each row is padded to a byte boundary
179             let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
180             let height = metrics.height as usize;
181             let data = image_data.read_array::<u8>(pitch * height)?;
182             Ok(BitmapData {
183                 metrics: BitmapMetrics::Small(metrics),
184                 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
185             })
186         }
187         // Small metrics, bit-aligned data
188         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-2-small-metrics-bit-aligned-data>
189         2 => {
190             let metrics = read_small_metrics(&mut image_data)?;
191             let width = metrics.width as usize * location.bit_depth as usize;
192             let height = metrics.height as usize;
193             // The data is tightly packed
194             let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
195             Ok(BitmapData {
196                 metrics: BitmapMetrics::Small(metrics),
197                 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
198             })
199         }
200         // Format 3 is obsolete
201         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-3-obsolete>
202         // Format 4 is not supported
203         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-4-not-supported-metrics-in-eblc-compressed-data>
204         // ---
205         // Metrics in EBLC/CBLC, bit-aligned image data only
206         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-5-metrics-in-eblc-bit-aligned-image-data-only>
207         5 => {
208             let metrics = location.metrics.clone().ok_or(ReadError::MalformedData(
209                 "expected metrics from location table",
210             ))?;
211             let width = metrics.width as usize * location.bit_depth as usize;
212             let height = metrics.height as usize;
213             // The data is tightly packed
214             let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
215             Ok(BitmapData {
216                 metrics: BitmapMetrics::Big(metrics),
217                 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
218             })
219         }
220         // Big metrics, byte-aligned data
221         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-6-big-metrics-byte-aligned-data>
222         6 => {
223             let metrics = read_big_metrics(&mut image_data)?;
224             // The data for each row is padded to a byte boundary
225             let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
226             let height = metrics.height as usize;
227             let data = image_data.read_array::<u8>(pitch * height)?;
228             Ok(BitmapData {
229                 metrics: BitmapMetrics::Big(metrics),
230                 content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
231             })
232         }
233         // Big metrics, bit-aligned data
234         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format7-big-metrics-bit-aligned-data>
235         7 => {
236             let metrics = read_big_metrics(&mut image_data)?;
237             let width = metrics.width as usize * location.bit_depth as usize;
238             let height = metrics.height as usize;
239             // The data is tightly packed
240             let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
241             Ok(BitmapData {
242                 metrics: BitmapMetrics::Big(metrics),
243                 content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
244             })
245         }
246         // Small metrics, component data
247         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-8-small-metrics-component-data>
248         8 => {
249             let metrics = read_small_metrics(&mut image_data)?;
250             let _pad = image_data.read::<u8>()?;
251             let count = image_data.read::<u16>()? as usize;
252             let components = image_data.read_array::<BdtComponent>(count)?;
253             Ok(BitmapData {
254                 metrics: BitmapMetrics::Small(metrics),
255                 content: BitmapContent::Composite(components),
256             })
257         }
258         // Big metrics, component data
259         // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-9-big-metrics-component-data>
260         9 => {
261             let metrics = read_big_metrics(&mut image_data)?;
262             let count = image_data.read::<u16>()? as usize;
263             let components = image_data.read_array::<BdtComponent>(count)?;
264             Ok(BitmapData {
265                 metrics: BitmapMetrics::Big(metrics),
266                 content: BitmapContent::Composite(components),
267             })
268         }
269         // Small metrics, PNG image data
270         // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-17-small-metrics-png-image-data>
271         17 if is_color => {
272             let metrics = read_small_metrics(&mut image_data)?;
273             let data_len = image_data.read::<u32>()? as usize;
274             let data = image_data.read_array::<u8>(data_len)?;
275             Ok(BitmapData {
276                 metrics: BitmapMetrics::Small(metrics),
277                 content: BitmapContent::Data(BitmapDataFormat::Png, data),
278             })
279         }
280         // Big metrics, PNG image data
281         // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-18-big-metrics-png-image-data>
282         18 if is_color => {
283             let metrics = read_big_metrics(&mut image_data)?;
284             let data_len = image_data.read::<u32>()? as usize;
285             let data = image_data.read_array::<u8>(data_len)?;
286             Ok(BitmapData {
287                 metrics: BitmapMetrics::Big(metrics),
288                 content: BitmapContent::Data(BitmapDataFormat::Png, data),
289             })
290         }
291         // Metrics in CBLC table, PNG image data
292         // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-19-metrics-in-cblc-table-png-image-data>
293         19 if is_color => {
294             let metrics = location.metrics.clone().ok_or(ReadError::MalformedData(
295                 "expected metrics from location table",
296             ))?;
297             let data_len = image_data.read::<u32>()? as usize;
298             let data = image_data.read_array::<u8>(data_len)?;
299             Ok(BitmapData {
300                 metrics: BitmapMetrics::Big(metrics),
301                 content: BitmapContent::Data(BitmapDataFormat::Png, data),
302             })
303         }
304         _ => Err(ReadError::MalformedData("unexpected bitmap data format")),
305     }
306 }
307 
read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError>308 fn read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError> {
309     Ok(cursor.read_array::<SmallGlyphMetrics>(1)?[0].clone())
310 }
311 
read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError>312 fn read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError> {
313     Ok(cursor.read_array::<BigGlyphMetrics>(1)?[0].clone())
314 }
315 
316 #[cfg(feature = "traversal")]
317 impl SbitLineMetrics {
traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a>318     pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> {
319         FieldType::Record(self.traverse(data))
320     }
321 }
322