Skip to main content

read_fonts/ps/cff/
fd_select.rs

1//! Parsing for CFF FDSelect tables.
2
3use types::GlyphId;
4
5#[doc(inline)]
6pub use super::v1::{
7    FdSelect, FdSelectFormat0 as Format0, FdSelectFormat3 as Format3, FdSelectFormat4 as Format4,
8    FdSelectRange3 as Range3, FdSelectRange4 as Range4,
9};
10
11impl FdSelect<'_> {
12    /// Returns the associated font DICT index for the given glyph identifier.
13    pub fn font_index(&self, glyph_id: GlyphId) -> Option<u16> {
14        match self {
15            // See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-11-fdselect-format-0>
16            Self::Format0(fds) => fds
17                .fds()
18                .get(glyph_id.to_u32() as usize)
19                .map(|fd| *fd as u16),
20            // See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-12-fdselect-format-3>
21            Self::Format3(fds) => {
22                let ranges = fds.ranges();
23                let gid = glyph_id.to_u32();
24                let ix = match ranges.binary_search_by(|range| (range.first() as u32).cmp(&gid)) {
25                    Ok(ix) => ix,
26                    Err(ix) => ix.saturating_sub(1),
27                };
28                Some(ranges.get(ix)?.fd() as u16)
29            }
30            // See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-14-fdselect-format-4>
31            Self::Format4(fds) => {
32                let ranges = fds.ranges();
33                let gid = glyph_id.to_u32();
34                let ix = match ranges.binary_search_by(|range| range.first().cmp(&gid)) {
35                    Ok(ix) => ix,
36                    Err(ix) => ix.saturating_sub(1),
37                };
38                Some(ranges.get(ix)?.fd())
39            }
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use font_test_data::bebuffer::BeBuffer;
47
48    use super::{FdSelect, GlyphId};
49    use crate::FontRead;
50    use std::ops::Range;
51
52    #[test]
53    fn select_font_index() {
54        let map = &[
55            (0..10, 0),
56            (10..32, 4),
57            (32..34, 1),
58            (34..128, 12),
59            (128..1024, 2),
60        ];
61        for data in make_fd_selects(map) {
62            let fd_select = FdSelect::read(data.data().into()).unwrap();
63            for (range, font_index) in map {
64                for gid in range.clone() {
65                    assert_eq!(
66                        fd_select.font_index(GlyphId::from(gid)).unwrap() as u8,
67                        *font_index
68                    )
69                }
70            }
71        }
72    }
73
74    /// Builds FDSelect structures in all three formats for the given
75    /// Range<GID> -> font index mapping.
76    fn make_fd_selects(map: &[(Range<u16>, u8)]) -> [BeBuffer; 3] {
77        let glyph_count = map.last().unwrap().0.end;
78        let format0 = {
79            let mut buf = BeBuffer::new();
80            buf = buf.push(0u8);
81            let mut fds = vec![0u8; glyph_count as usize];
82            for (range, font_index) in map {
83                for gid in range.clone() {
84                    fds[gid as usize] = *font_index;
85                }
86            }
87            buf = buf.extend(fds);
88            buf
89        };
90        let format3 = {
91            let mut buf = BeBuffer::new();
92            buf = buf.push(3u8);
93            buf = buf.push(map.len() as u16);
94            for (range, font_index) in map {
95                buf = buf.push(range.start);
96                buf = buf.push(*font_index);
97            }
98            buf = buf.push(glyph_count);
99            buf
100        };
101        let format4 = {
102            let mut buf = BeBuffer::new();
103            buf = buf.push(4u8);
104            buf = buf.push(map.len() as u32);
105            for (range, font_index) in map {
106                buf = buf.push(range.start as u32);
107                buf = buf.push(*font_index as u16);
108            }
109            buf = buf.push(glyph_count as u32);
110            buf
111        };
112        [format0, format3, format4]
113    }
114}