ttf_parser/tables/
sbix.rs

1//! A [Standard Bitmap Graphics Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/sbix) implementation.
3
4use core::convert::TryFrom;
5use core::num::NonZeroU16;
6
7use crate::{GlyphId, RasterGlyphImage, RasterImageFormat, Tag};
8use crate::parser::{Stream, FromData, Offset, Offset32, LazyArray16, LazyArray32};
9
10/// A strike of glyphs.
11#[derive(Clone, Copy)]
12pub struct Strike<'a> {
13    /// The pixels per EM size for which this strike was designed.
14    pub pixels_per_em: u16,
15    /// The device pixel density (in PPI) for which this strike was designed.
16    pub ppi: u16,
17    offsets: LazyArray16<'a, Offset32>,
18    /// Data from the beginning of the `Strikes` table.
19    data: &'a [u8],
20}
21
22impl<'a> Strike<'a> {
23    fn parse(number_of_glyphs: u16, data: &'a [u8]) -> Option<Self> {
24        let mut s = Stream::new(data);
25        let pixels_per_em = s.read::<u16>()?;
26        let ppi = s.read::<u16>()?;
27        let offsets = s.read_array16(number_of_glyphs)?;
28        Some(Strike {
29            pixels_per_em,
30            ppi,
31            offsets,
32            data,
33        })
34    }
35
36    /// Returns a glyph data.
37    pub fn get(&self, glyph_id: GlyphId) -> Option<RasterGlyphImage<'a>> {
38        self.get_inner(glyph_id, 0)
39    }
40
41    fn get_inner(&self, glyph_id: GlyphId, depth: u8) -> Option<RasterGlyphImage<'a>> {
42        // Recursive `dupe`. Bail.
43        if depth == 10 {
44            return None;
45        }
46
47        let start = self.offsets.get(glyph_id.0)?.to_usize();
48        let end = self.offsets.get(glyph_id.0.checked_add(1)?)?.to_usize();
49
50        if start == end {
51            return None;
52        }
53
54        let data_len = end.checked_sub(start)?.checked_sub(8)?; // 8 is a Glyph data header size.
55
56        let mut s = Stream::new_at(self.data, start)?;
57        let x = s.read::<i16>()?;
58        let y = s.read::<i16>()?;
59        let image_type = s.read::<Tag>()?;
60        let image_data = s.read_bytes(data_len)?;
61
62        // We do ignore `pdf` and `mask` intentionally, because Apple docs state that:
63        // 'Support for the 'pdf ' and 'mask' data types and sbixDrawOutlines flag
64        // are planned for future releases of iOS and OS X.'
65        let format = match &image_type.to_bytes() {
66            b"png " => RasterImageFormat::PNG,
67            b"dupe" => {
68                // 'The special graphicType of 'dupe' indicates that
69                // the data field contains a glyph ID. The bitmap data for
70                // the indicated glyph should be used for the current glyph.'
71                let glyph_id = GlyphId::parse(image_data)?;
72                // TODO: The spec isn't clear about which x/y values should we use.
73                //       The current glyph or the referenced one.
74                return self.get_inner(glyph_id, depth + 1);
75            }
76            _ => {
77                // TODO: support JPEG and TIFF
78                return None;
79            }
80        };
81
82        let (width, height) = png_size(image_data)?;
83
84        Some(RasterGlyphImage {
85            x,
86            y,
87            width,
88            height,
89            pixels_per_em: self.pixels_per_em,
90            format,
91            data: image_data,
92        })
93    }
94
95    /// Returns the number of glyphs in this strike.
96    #[inline]
97    pub fn len(&self) -> u16 {
98        // The last offset simply indicates the glyph data end. We don't need it.
99        self.offsets.len() - 1
100    }
101}
102
103impl core::fmt::Debug for Strike<'_> {
104    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
105        write!(f, "Strike {{ ... }}")
106    }
107}
108
109
110/// A list of [`Strike`]s.
111#[derive(Clone, Copy)]
112pub struct Strikes<'a> {
113    /// `sbix` table data.
114    data: &'a [u8],
115    // Offsets from the beginning of the `sbix` table.
116    offsets: LazyArray32<'a, Offset32>,
117    // The total number of glyphs in the face + 1. From the `maxp` table.
118    number_of_glyphs: u16,
119}
120
121impl<'a> Strikes<'a> {
122    /// Returns a strike at the index.
123    pub fn get(&self, index: u32) -> Option<Strike<'a>> {
124        let offset = self.offsets.get(index)?.to_usize();
125        let data = self.data.get(offset..)?;
126        Strike::parse(self.number_of_glyphs, data)
127    }
128
129    /// Returns the number of strikes.
130    #[inline]
131    pub fn len(&self) -> u32 {
132        self.offsets.len()
133    }
134}
135
136impl core::fmt::Debug for Strikes<'_> {
137    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
138        write!(f, "Strikes {{ ... }}")
139    }
140}
141
142impl<'a> IntoIterator for Strikes<'a> {
143    type Item = Strike<'a>;
144    type IntoIter = StrikesIter<'a>;
145
146    #[inline]
147    fn into_iter(self) -> Self::IntoIter {
148        StrikesIter {
149            strikes: self,
150            index: 0,
151        }
152    }
153}
154
155/// An iterator over [`Strikes`].
156#[allow(missing_debug_implementations)]
157pub struct StrikesIter<'a> {
158    strikes: Strikes<'a>,
159    index: u32,
160}
161
162impl<'a> Iterator for StrikesIter<'a> {
163    type Item = Strike<'a>;
164
165    fn next(&mut self) -> Option<Self::Item> {
166        if self.index < self.strikes.len() {
167            self.index += 1;
168            self.strikes.get(self.index - 1)
169        } else {
170            None
171        }
172    }
173}
174
175
176/// A [Standard Bitmap Graphics Table](
177/// https://docs.microsoft.com/en-us/typography/opentype/spec/sbix).
178#[derive(Clone, Copy, Debug)]
179pub struct Table<'a> {
180    /// A list of [`Strike`]s.
181    pub strikes: Strikes<'a>,
182}
183
184impl<'a> Table<'a> {
185    /// Parses a table from raw data.
186    ///
187    /// - `number_of_glyphs` is from the `maxp` table.
188    pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
189        let number_of_glyphs = number_of_glyphs.get().checked_add(1)?;
190
191        let mut s = Stream::new(data);
192
193        let version = s.read::<u16>()?;
194        if version != 1 {
195            return None;
196        }
197
198        s.skip::<u16>(); // flags
199
200        let strikes_count = s.read::<u32>()?;
201        if strikes_count == 0 {
202            return None;
203        }
204
205        let offsets = s.read_array32::<Offset32>(strikes_count)?;
206
207        Some(Table {
208            strikes: Strikes {
209                data,
210                offsets,
211                number_of_glyphs,
212            },
213        })
214    }
215
216    /// Selects the best matching [`Strike`] based on `pixels_per_em`.
217    pub fn best_strike(&self, pixels_per_em: u16) -> Option<Strike<'a>> {
218        let mut idx = 0;
219        let mut max_ppem = 0;
220        for (i, strike) in self.strikes.into_iter().enumerate() {
221            if (pixels_per_em <= strike.pixels_per_em && strike.pixels_per_em < max_ppem) ||
222                (pixels_per_em > max_ppem && strike.pixels_per_em > max_ppem)
223            {
224                idx = i as u32;
225                max_ppem = strike.pixels_per_em;
226            }
227        }
228
229        self.strikes.get(idx)
230    }
231}
232
233// The `sbix` table doesn't store the image size, so we have to parse it manually.
234// Which is quite simple in case of PNG, but way more complex for JPEG.
235// Therefore we are omitting it for now.
236fn png_size(data: &[u8]) -> Option<(u16, u16)> {
237    // PNG stores its size as u32 BE at a fixed offset.
238    let mut s = Stream::new_at(data, 16)?;
239    let width = s.read::<u32>()?;
240    let height = s.read::<u32>()?;
241
242    // PNG size larger than u16::MAX is an error.
243    Some((
244        u16::try_from(width).ok()?,
245        u16::try_from(height).ok()?,
246    ))
247}