1
#![deny(missing_docs)]
2

            
3
//! Standard Bitmap Graphics Table (`sbix`).
4
//!
5
//! References:
6
//!
7
//! * [Microsoft](https://docs.microsoft.com/en-us/typography/opentype/spec/sbix)
8
//! * [Apple](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html)
9

            
10
use super::{
11
    BitDepth, Bitmap, BitmapGlyph, EncapsulatedBitmap, EncapsulatedFormat, Metrics, OriginOffset,
12
};
13
use crate::binary::read::{ReadArray, ReadBinaryDep, ReadCtxt, ReadScope};
14
use crate::binary::U32Be;
15
use crate::error::ParseError;
16
use crate::tag;
17
use crate::SafeFrom;
18

            
19
/// `sbix` table containing bitmaps.
20
pub struct Sbix<'a> {
21
    /// Drawing flags.
22
    ///
23
    /// * Bit 0: Set to 1.
24
    /// * Bit 1: Draw outlines.
25
    /// * Bits 2 to 15: reserved (set to 0).
26
    pub flags: u16,
27
    /// Bitmap data for different sizes.
28
    pub strikes: Vec<SbixStrike<'a>>,
29
}
30

            
31
/// A single strike (ppem/ppi) combination.
32
pub struct SbixStrike<'a> {
33
    scope: ReadScope<'a>,
34
    /// The PPEM size for which this strike was designed.
35
    pub ppem: u16,
36
    /// The device pixel density (in pixels-per-inch) for which this strike was designed.
37
    pub ppi: u16,
38
    /// Offset from the beginning of the strike data header to bitmap data for an individual glyph.
39
    glyph_data_offsets: ReadArray<'a, U32Be>,
40
}
41

            
42
/// An `sbix` bitmap glyph.
43
pub struct SbixGlyph<'a> {
44
    /// The horizontal (x-axis) offset from the left edge of the graphic to the glyph’s origin.
45
    ///
46
    /// That is, the x-coordinate of the point on the baseline at the left edge of the glyph.
47
    pub origin_offset_x: i16,
48
    /// The vertical (y-axis) offset from the bottom edge of the graphic to the glyph’s origin.
49
    ///
50
    /// That is, the y-coordinate of the point on the baseline at the left edge of the glyph.
51
    pub origin_offset_y: i16,
52
    /// Indicates the format of the embedded graphic data.
53
    ///
54
    /// One of `jpg `, `png ` or `tiff`; or the special formats `dupe` or `flip`.
55
    pub graphic_type: u32,
56
    /// The actual embedded graphic data.
57
    pub data: &'a [u8],
58
}
59

            
60
impl ReadBinaryDep for Sbix<'_> {
61
    type Args<'a> = usize; // num_glyphs
62
    type HostType<'a> = Sbix<'a>;
63

            
64
    /// Read the `sbix` table.
65
    ///
66
    /// `num_glyphs` should be read from the `maxp` table.
67
    fn read_dep<'a>(
68
        ctxt: &mut ReadCtxt<'a>,
69
        num_glyphs: usize,
70
    ) -> Result<Self::HostType<'a>, ParseError> {
71
        let scope = ctxt.scope();
72
        let version = ctxt.read_u16be()?;
73
        ctxt.check(version == 1)?;
74
        let flags = ctxt.read_u16be()?;
75
        let num_strikes = usize::try_from(ctxt.read_u32be()?)?;
76
        let strike_offsets = ctxt.read_array::<U32Be>(num_strikes)?;
77
        let strikes = strike_offsets
78
            .iter()
79
            .map(|offset| {
80
                let offset = usize::try_from(offset)?;
81
                scope.offset(offset).read_dep::<SbixStrike<'_>>(num_glyphs)
82
            })
83
            .collect::<Result<Vec<_>, _>>()?;
84

            
85
        Ok(Sbix { flags, strikes })
86
    }
87
}
88

            
89
impl ReadBinaryDep for SbixStrike<'_> {
90
    type Args<'a> = usize; // num_glyphs
91
    type HostType<'a> = SbixStrike<'a>;
92

            
93
    fn read_dep<'a>(
94
        ctxt: &mut ReadCtxt<'a>,
95
        num_glyphs: usize,
96
    ) -> Result<Self::HostType<'a>, ParseError> {
97
        let scope = ctxt.scope();
98
        let ppem = ctxt.read_u16be()?;
99
        let ppi = ctxt.read_u16be()?;
100
        // The glyph_data_offset array includes offsets for every glyph ID, plus one extra.
101
        // — https://docs.microsoft.com/en-us/typography/opentype/spec/sbix#strikes
102
        let glyph_data_offsets = ctxt.read_array::<U32Be>(num_glyphs + 1)?;
103

            
104
        Ok(SbixStrike {
105
            scope,
106
            ppem,
107
            ppi,
108
            glyph_data_offsets,
109
        })
110
    }
111
}
112

            
113
impl ReadBinaryDep for SbixGlyph<'_> {
114
    type Args<'a> = usize; // data_len
115
    type HostType<'a> = SbixGlyph<'a>;
116

            
117
    fn read_dep<'a>(
118
        ctxt: &mut ReadCtxt<'a>,
119
        length: usize,
120
    ) -> Result<Self::HostType<'a>, ParseError> {
121
        let origin_offset_x = ctxt.read_i16be()?;
122
        let origin_offset_y = ctxt.read_i16be()?;
123
        let graphic_type = ctxt.read_u32be()?;
124
        // The length of the glyph data is the length of the glyph minus the preceding
125
        // header fields:
126
        // * u16 origin_offset_x  = 2
127
        // * u16 origin_offset_y  = 2
128
        // * u32 graphic_type     = 4
129
        // TOTAL:                 = 8
130
        let data = ctxt.read_slice(length - 8)?;
131

            
132
        Ok(SbixGlyph {
133
            origin_offset_x,
134
            origin_offset_y,
135
            graphic_type,
136
            data,
137
        })
138
    }
139
}
140

            
141
impl<'a> Sbix<'a> {
142
    /// Find a strike matching the desired parameters
143
    pub fn find_strike(
144
        &self,
145
        glyph_index: u16,
146
        target_ppem: u16,
147
        _max_bit_depth: BitDepth,
148
    ) -> Option<&SbixStrike<'a>> {
149
        // A strike does not need to include data for every glyph, and does not need to include
150
        // data for the same set of glyphs as other strikes. If the application is using bitmap
151
        // data to draw text and there is bitmap data for a glyph in any strike, then the glyph
152
        // must be drawn using a bitmap from some strike.
153
        //
154
        // If the exact size is not available, implementations may choose a bitmap based on the
155
        // closest available larger size, or the closest available integer-multiple larger size, or
156
        // on some other basis. The only cases in which a glyph is not drawn using a bitmap are if
157
        // the application has not requested that text be drawn using bitmap data or if there is no
158
        // bitmap data for the glyph in any strike.
159
        //
160
        // — https://docs.microsoft.com/en-us/typography/opentype/spec/sbix#strikes
161
        let target_ppem = i32::from(target_ppem);
162
        let candidates = self
163
            .strikes
164
            .iter()
165
            .filter(|strike| strike.contains_glyph(glyph_index));
166

            
167
        let mut best = None;
168
        for strike in candidates {
169
            let strike_ppem = i32::from(strike.ppem);
170
            let difference = strike_ppem - target_ppem;
171
            match best {
172
                Some((current_best_difference, _)) => {
173
                    if super::bigger_or_closer_to_zero(strike_ppem, current_best_difference) {
174
                        best = Some((difference, strike))
175
                    }
176
                }
177
                None => best = Some((difference, strike)),
178
            }
179
        }
180

            
181
        best.map(|(_, strike)| strike)
182
    }
183
}
184

            
185
impl<'a> SbixStrike<'a> {
186
    /// Read a glyph from this strike specified by `glyph_index`.
187
    pub fn read_glyph(&self, glyph_index: u16) -> Result<Option<SbixGlyph<'a>>, ParseError> {
188
        let (offset, end) = self.glyph_offset_end(glyph_index)?;
189
        match end.checked_sub(offset) {
190
            Some(0) => {
191
                // If this is zero, there is no bitmap data for that glyph in this strike.
192
                // — https://docs.microsoft.com/en-us/typography/opentype/spec/sbix#strikes
193
                Ok(None)
194
            }
195
            Some(length) => {
196
                let data = self.scope.offset_length(offset, length)?;
197
                data.read_dep::<SbixGlyph<'_>>(length).map(Some)
198
            }
199
            None => Err(ParseError::BadOffset),
200
        }
201
    }
202

            
203
    fn glyph_offset_end(&self, glyph_index: u16) -> Result<(usize, usize), ParseError> {
204
        // The length of the bitmap data for each glyph is variable, and can be determined from the
205
        // difference between two consecutive offsets. Hence, the length of data for glyph N is
206
        // glyph_data_offset[N+1] - glyph_data_offset[N].
207
        // — https://docs.microsoft.com/en-us/typography/opentype/spec/sbix#strikes
208
        let glyph_index = usize::from(glyph_index);
209
        let offset = self
210
            .glyph_data_offsets
211
            .get_item(glyph_index)
212
            .map(SafeFrom::safe_from)
213
            .ok_or(ParseError::BadIndex)?;
214
        let end = self
215
            .glyph_data_offsets
216
            .get_item(glyph_index + 1)
217
            .map(SafeFrom::safe_from)
218
            .ok_or(ParseError::BadIndex)?;
219
        Ok((offset, end))
220
    }
221

            
222
    fn contains_glyph(&self, glyph_index: u16) -> bool {
223
        self.glyph_offset_end(glyph_index)
224
            .map(|(offset, end)| end.saturating_sub(offset) > 0)
225
            .unwrap_or(false)
226
    }
227
}
228

            
229
impl<'a> From<(&SbixStrike<'a>, &SbixGlyph<'a>, u16, bool)> for BitmapGlyph {
230
    fn from(
231
        (strike, glyph, bitmap_id, should_flip_hori): (&SbixStrike<'a>, &SbixGlyph<'a>, u16, bool),
232
    ) -> Self {
233
        let encapsulated = EncapsulatedBitmap {
234
            format: EncapsulatedFormat::from(glyph.graphic_type),
235
            data: Box::from(glyph.data),
236
        };
237
        BitmapGlyph {
238
            bitmap: Bitmap::Encapsulated(encapsulated),
239
            bitmap_id,
240
            metrics: Metrics::HmtxVmtx(OriginOffset::from(glyph)),
241
            ppem_x: Some(strike.ppem),
242
            ppem_y: Some(strike.ppem),
243
            should_flip_hori,
244
        }
245
    }
246
}
247

            
248
impl From<u32> for EncapsulatedFormat {
249
    fn from(tag: u32) -> Self {
250
        match tag {
251
            tag::JPG => EncapsulatedFormat::Jpeg,
252
            tag::PNG => EncapsulatedFormat::Png,
253
            tag::TIFF => EncapsulatedFormat::Tiff,
254
            _ => EncapsulatedFormat::Other(tag),
255
        }
256
    }
257
}
258

            
259
impl From<&SbixGlyph<'_>> for OriginOffset {
260
    fn from(glyph: &SbixGlyph<'_>) -> Self {
261
        OriginOffset {
262
            x: glyph.origin_offset_x,
263
            y: glyph.origin_offset_y,
264
        }
265
    }
266
}
267

            
268
#[cfg(test)]
269
mod tests {
270
    use super::*;
271

            
272
    use crate::binary::read::ReadScope;
273
    use crate::font_data::FontData;
274
    use crate::tables::{FontTableProvider, MaxpTable};
275
    use crate::tag;
276

            
277
    use crate::tests::read_fixture;
278

            
279
    #[test]
280
    fn test_read_sbix() {
281
        let buffer = read_fixture("tests/fonts/woff1/chromacheck-sbix.woff");
282
        let scope = ReadScope::new(&buffer);
283
        let font_file = scope
284
            .read::<FontData<'_>>()
285
            .expect("unable to parse font file");
286
        let table_provider = font_file
287
            .table_provider(0)
288
            .expect("unable to create font provider");
289
        let maxp_data = table_provider
290
            .read_table_data(tag::MAXP)
291
            .expect("unable to read maxp table data");
292
        let maxp = ReadScope::new(&maxp_data).read::<MaxpTable>().unwrap();
293
        let sbix_data = table_provider
294
            .read_table_data(tag::SBIX)
295
            .expect("unable to read sbix table data");
296
        let sbix = ReadScope::new(&sbix_data)
297
            .read_dep::<Sbix<'_>>(usize::try_from(maxp.num_glyphs).unwrap())
298
            .unwrap();
299

            
300
        // Header
301
        assert_eq!(sbix.flags, 1);
302

            
303
        // Strikes
304
        assert_eq!(sbix.strikes.len(), 1);
305
        let strike = &sbix.strikes[0];
306
        assert_eq!(strike.ppem, 300);
307
        assert_eq!(strike.ppi, 72);
308

            
309
        // Glyphs
310
        let glyphs = (0..maxp.num_glyphs)
311
            .map(|glyph_index| strike.read_glyph(glyph_index))
312
            .collect::<Result<Vec<_>, _>>()
313
            .expect("unable to read glyph");
314

            
315
        assert_eq!(glyphs.len(), 2);
316
        assert!(glyphs[0].is_none());
317
        if let Some(ref glyph) = glyphs[1] {
318
            assert_eq!(glyph.origin_offset_x, 0);
319
            assert_eq!(glyph.origin_offset_y, 0);
320
            assert_eq!(glyph.graphic_type, tag::PNG);
321
            assert_eq!(glyph.data.len(), 224);
322
            assert_eq!(*glyph.data.last().unwrap(), 0x82);
323
        } else {
324
            panic!("expected Some(SbixGlyph) got None");
325
        }
326
    }
327
}