allsorts_subset_browser/bitmap/
sbix.rs1#![deny(missing_docs)]
2
3use std::convert::TryFrom;
11
12use super::{
13 BitDepth, Bitmap, BitmapGlyph, EncapsulatedBitmap, EncapsulatedFormat, Metrics, OriginOffset,
14};
15use crate::binary::read::{CheckIndex, ReadArray, ReadBinaryDep, ReadCtxt, ReadScope};
16use crate::binary::U32Be;
17use crate::error::ParseError;
18use crate::tag;
19
20pub struct Sbix<'a> {
22 pub flags: u16,
28 pub strikes: Vec<SbixStrike<'a>>,
30}
31
32pub struct SbixStrike<'a> {
34 scope: ReadScope<'a>,
35 pub ppem: u16,
37 pub ppi: u16,
39 glyph_data_offsets: ReadArray<'a, U32Be>,
41}
42
43pub struct SbixGlyph<'a> {
45 pub origin_offset_x: i16,
49 pub origin_offset_y: i16,
53 pub graphic_type: u32,
57 pub data: &'a [u8],
59}
60
61impl<'b> ReadBinaryDep for Sbix<'b> {
62 type Args<'a> = usize; type HostType<'a> = Sbix<'a>;
64
65 fn read_dep<'a>(
69 ctxt: &mut ReadCtxt<'a>,
70 num_glyphs: usize,
71 ) -> Result<Self::HostType<'a>, ParseError> {
72 let scope = ctxt.scope();
73 let version = ctxt.read_u16be()?;
74 ctxt.check(version == 1)?;
75 let flags = ctxt.read_u16be()?;
76 let num_strikes = usize::try_from(ctxt.read_u32be()?)?;
77 let strike_offsets = ctxt.read_array::<U32Be>(num_strikes)?;
78 let strikes = strike_offsets
79 .iter()
80 .map(|offset| {
81 let offset = usize::try_from(offset)?;
82 scope.offset(offset).read_dep::<SbixStrike<'_>>(num_glyphs)
83 })
84 .collect::<Result<Vec<_>, _>>()?;
85
86 Ok(Sbix { flags, strikes })
87 }
88}
89
90impl<'b> ReadBinaryDep for SbixStrike<'b> {
91 type Args<'a> = usize; type HostType<'a> = SbixStrike<'a>;
93
94 fn read_dep<'a>(
95 ctxt: &mut ReadCtxt<'a>,
96 num_glyphs: usize,
97 ) -> Result<Self::HostType<'a>, ParseError> {
98 let scope = ctxt.scope();
99 let ppem = ctxt.read_u16be()?;
100 let ppi = ctxt.read_u16be()?;
101 let glyph_data_offsets = ctxt.read_array::<U32Be>(num_glyphs + 1)?;
104
105 Ok(SbixStrike {
106 scope,
107 ppem,
108 ppi,
109 glyph_data_offsets,
110 })
111 }
112}
113
114impl<'b> ReadBinaryDep for SbixGlyph<'b> {
115 type Args<'a> = usize; type HostType<'a> = SbixGlyph<'a>;
117
118 fn read_dep<'a>(
119 ctxt: &mut ReadCtxt<'a>,
120 length: usize,
121 ) -> Result<Self::HostType<'a>, ParseError> {
122 let origin_offset_x = ctxt.read_i16be()?;
123 let origin_offset_y = ctxt.read_i16be()?;
124 let graphic_type = ctxt.read_u32be()?;
125 let data = ctxt.read_slice(length - 8)?;
132
133 Ok(SbixGlyph {
134 origin_offset_x,
135 origin_offset_y,
136 graphic_type,
137 data,
138 })
139 }
140}
141
142impl<'a> Sbix<'a> {
143 pub fn find_strike(
145 &self,
146 glyph_index: u16,
147 target_ppem: u16,
148 _max_bit_depth: BitDepth,
149 ) -> Option<&SbixStrike<'a>> {
150 let target_ppem = i32::from(target_ppem);
163 let candidates = self
164 .strikes
165 .iter()
166 .filter(|strike| strike.contains_glyph(glyph_index));
167
168 let mut best = None;
169 for strike in candidates {
170 let strike_ppem = i32::from(strike.ppem);
171 let difference = strike_ppem - target_ppem;
172 match best {
173 Some((current_best_difference, _)) => {
174 if super::bigger_or_closer_to_zero(strike_ppem, current_best_difference) {
175 best = Some((difference, strike))
176 }
177 }
178 None => best = Some((difference, strike)),
179 }
180 }
181
182 best.map(|(_, strike)| strike)
183 }
184}
185
186impl<'a> SbixStrike<'a> {
187 pub fn read_glyph(&self, glyph_index: u16) -> Result<Option<SbixGlyph<'a>>, ParseError> {
189 let (offset, end) = self.glyph_offset_end(glyph_index)?;
190 match end.checked_sub(offset) {
191 Some(0) => {
192 Ok(None)
195 }
196 Some(length) => {
197 let data = self.scope.offset_length(offset, length)?;
198 data.read_dep::<SbixGlyph<'_>>(length).map(Some)
199 }
200 None => Err(ParseError::BadOffset),
201 }
202 }
203
204 fn glyph_offset_end(&self, glyph_index: u16) -> Result<(usize, usize), ParseError> {
205 let glyph_index = usize::from(glyph_index);
210 self.glyph_data_offsets.check_index(glyph_index)?;
211 self.glyph_data_offsets.check_index(glyph_index + 1)?;
212 let offset = usize::try_from(self.glyph_data_offsets.get_item(glyph_index))?;
213 let end = usize::try_from(self.glyph_data_offsets.get_item(glyph_index + 1))?;
214 Ok((offset, end))
215 }
216
217 fn contains_glyph(&self, glyph_index: u16) -> bool {
218 self.glyph_offset_end(glyph_index)
219 .map(|(offset, end)| end.saturating_sub(offset) > 0)
220 .unwrap_or(false)
221 }
222}
223
224impl<'a> From<(&SbixStrike<'a>, &SbixGlyph<'a>)> for BitmapGlyph {
225 fn from((strike, glyph): (&SbixStrike<'a>, &SbixGlyph<'a>)) -> Self {
226 let encapsulated = EncapsulatedBitmap {
227 format: EncapsulatedFormat::from(glyph.graphic_type),
228 data: Box::from(glyph.data),
229 };
230 BitmapGlyph {
231 bitmap: Bitmap::Encapsulated(encapsulated),
232 metrics: Metrics::HmtxVmtx(OriginOffset::from(glyph)),
233 ppem_x: Some(strike.ppem),
234 ppem_y: Some(strike.ppem),
235 }
236 }
237}
238
239impl From<u32> for EncapsulatedFormat {
240 fn from(tag: u32) -> Self {
241 match tag {
242 tag::JPG => EncapsulatedFormat::Jpeg,
243 tag::PNG => EncapsulatedFormat::Png,
244 tag::TIFF => EncapsulatedFormat::Tiff,
245 _ => EncapsulatedFormat::Other(tag),
246 }
247 }
248}
249
250impl From<&SbixGlyph<'_>> for OriginOffset {
251 fn from(glyph: &SbixGlyph<'_>) -> Self {
252 OriginOffset {
253 x: glyph.origin_offset_x,
254 y: glyph.origin_offset_y,
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 use crate::binary::read::ReadScope;
264 use crate::font_data::FontData;
265 use crate::tables::{FontTableProvider, MaxpTable};
266 use crate::tag;
267
268 use crate::tests::read_fixture;
269
270 #[test]
271 fn test_read_sbix() {
272 let buffer = read_fixture("tests/fonts/woff1/chromacheck-sbix.woff");
273 let scope = ReadScope::new(&buffer);
274 let font_file = scope
275 .read::<FontData<'_>>()
276 .expect("unable to parse font file");
277 let table_provider = font_file
278 .table_provider(0)
279 .expect("unable to create font provider");
280 let maxp_data = table_provider
281 .read_table_data(tag::MAXP)
282 .expect("unable to read maxp table data");
283 let maxp = ReadScope::new(&maxp_data).read::<MaxpTable>().unwrap();
284 let sbix_data = table_provider
285 .read_table_data(tag::SBIX)
286 .expect("unable to read sbix table data");
287 let sbix = ReadScope::new(&sbix_data)
288 .read_dep::<Sbix<'_>>(usize::try_from(maxp.num_glyphs).unwrap())
289 .unwrap();
290
291 assert_eq!(sbix.flags, 1);
293
294 assert_eq!(sbix.strikes.len(), 1);
296 let strike = &sbix.strikes[0];
297 assert_eq!(strike.ppem, 300);
298 assert_eq!(strike.ppi, 72);
299
300 let glyphs = (0..maxp.num_glyphs)
302 .map(|glyph_index| strike.read_glyph(glyph_index))
303 .collect::<Result<Vec<_>, _>>()
304 .expect("unable to read glyph");
305
306 assert_eq!(glyphs.len(), 2);
307 assert!(glyphs[0].is_none());
308 if let Some(ref glyph) = glyphs[1] {
309 assert_eq!(glyph.origin_offset_x, 0);
310 assert_eq!(glyph.origin_offset_y, 0);
311 assert_eq!(glyph.graphic_type, tag::PNG);
312 assert_eq!(glyph.data.len(), 224);
313 assert_eq!(*glyph.data.last().unwrap(), 0x82);
314 } else {
315 panic!("expected Some(SbixGlyph) got None");
316 }
317 }
318}