read_fonts/ps/cff/
fd_select.rs1use 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 pub fn font_index(&self, glyph_id: GlyphId) -> Option<u16> {
14 match self {
15 Self::Format0(fds) => fds
17 .fds()
18 .get(glyph_id.to_u32() as usize)
19 .map(|fd| *fd as u16),
20 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 let range = ranges.get(ix)?;
29 if !(range.first() as u32..fds.sentinel() as u32).contains(&gid) {
30 return None;
31 }
32 Some(range.fd() as u16)
33 }
34 Self::Format4(fds) => {
36 let ranges = fds.ranges();
37 let gid = glyph_id.to_u32();
38 let ix = match ranges.binary_search_by(|range| range.first().cmp(&gid)) {
39 Ok(ix) => ix,
40 Err(ix) => ix.saturating_sub(1),
41 };
42 let range = ranges.get(ix)?;
43 if !(range.first()..fds.sentinel()).contains(&gid) {
44 return None;
45 }
46 Some(range.fd())
47 }
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use font_test_data::bebuffer::BeBuffer;
55
56 use super::{FdSelect, GlyphId};
57 use crate::FontRead;
58 use std::ops::Range;
59
60 #[test]
61 fn select_font_index() {
62 let map = &[
63 (0..10, 0),
64 (10..32, 4),
65 (32..34, 1),
66 (34..128, 12),
67 (128..1024, 2),
68 ];
69 for data in make_fd_selects(map) {
70 let fd_select = FdSelect::read(data.data().into()).unwrap();
71 for (range, font_index) in map {
72 for gid in range.clone() {
73 assert_eq!(
74 fd_select.font_index(GlyphId::from(gid)).unwrap() as u8,
75 *font_index
76 )
77 }
78 }
79 }
80 }
81
82 #[test]
83 fn select_font_index_out_of_bounds() {
84 let map = &[(3..10, 0), (10..20, 1)];
85 for data in make_fd_selects(map).iter().skip(1) {
88 let fd_select = FdSelect::read(data.data().into()).unwrap();
89 for gid in [0, 1, 2, 20, 40] {
90 assert!(fd_select.font_index(GlyphId::new(gid)).is_none());
91 }
92 }
93 }
94
95 fn make_fd_selects(map: &[(Range<u16>, u8)]) -> [BeBuffer; 3] {
98 let glyph_count = map.last().unwrap().0.end;
99 let format0 = {
100 let mut buf = BeBuffer::new();
101 buf = buf.push(0u8);
102 let mut fds = vec![0u8; glyph_count as usize];
103 for (range, font_index) in map {
104 for gid in range.clone() {
105 fds[gid as usize] = *font_index;
106 }
107 }
108 buf = buf.extend(fds);
109 buf
110 };
111 let format3 = {
112 let mut buf = BeBuffer::new();
113 buf = buf.push(3u8);
114 buf = buf.push(map.len() as u16);
115 for (range, font_index) in map {
116 buf = buf.push(range.start);
117 buf = buf.push(*font_index);
118 }
119 buf = buf.push(glyph_count);
120 buf
121 };
122 let format4 = {
123 let mut buf = BeBuffer::new();
124 buf = buf.push(4u8);
125 buf = buf.push(map.len() as u32);
126 for (range, font_index) in map {
127 buf = buf.push(range.start as u32);
128 buf = buf.push(*font_index as u16);
129 }
130 buf = buf.push(glyph_count as u32);
131 buf
132 };
133 [format0, format3, format4]
134 }
135}