ttf_parser/tables/
cblc.rs
1use crate::GlyphId;
4use crate::parser::{Stream, FromData, Offset, Offset16, Offset32, NumFrom};
5
6#[derive(Clone, Copy, PartialEq, Debug)]
7pub enum BitmapFormat {
8 Format17,
9 Format18,
10 Format19,
11}
12
13#[derive(Clone, Copy, Default, Debug)]
14pub struct Metrics {
15 pub x: i8,
16 pub y: i8,
17 pub width: u8,
18 pub height: u8,
19}
20
21#[derive(Clone, Copy, Debug)]
22pub struct Location {
23 pub format: BitmapFormat,
24 pub offset: usize,
25 pub metrics: Metrics,
26 pub ppem: u16,
27}
28
29pub fn find_location(
30 data: &[u8],
31 glyph_id: GlyphId,
32 pixels_per_em: u16,
33) -> Option<Location> {
34 let mut s = Stream::new(data);
35
36 s.skip::<u32>(); let size_table = select_bitmap_size_table(glyph_id, pixels_per_em, s)?;
44 let info = select_index_subtable(data, size_table, glyph_id)?;
45
46 let mut s = Stream::new_at(data, info.offset)?;
47 let index_format: u16 = s.read()?;
48 let image_format: u16 = s.read()?;
49 let mut image_offset = s.read::<Offset32>()?.to_usize();
50
51 let image_format = match image_format {
52 17 => BitmapFormat::Format17,
53 18 => BitmapFormat::Format18,
54 19 => BitmapFormat::Format19,
55 _ => return None, };
57
58 let glyph_diff = glyph_id.0.checked_sub(info.start_glyph_id.0)?;
61 let metrics = Metrics::default();
62 match index_format {
63 1 => {
64 s.advance(usize::from(glyph_diff) * Offset32::SIZE);
65 let offset: Offset32 = s.read()?;
66 image_offset += offset.to_usize();
67 }
68 2 => {
69 let image_size: u32 = s.read()?;
70 image_offset += usize::from(glyph_diff).checked_mul(usize::num_from(image_size))?;
71 }
72 3 => {
73 s.advance(usize::from(glyph_diff) * Offset16::SIZE);
74 let offset: Offset16 = s.read()?;
75 image_offset += offset.to_usize();
76 }
77 4 => {
78 let num_glyphs: u32 = s.read()?;
79 let num_glyphs = num_glyphs.checked_add(1)?;
80 let pairs = s.read_array32::<GlyphIdOffsetPair>(num_glyphs)?;
81 let pair = pairs.into_iter().find(|pair| pair.glyph_id == glyph_id)?;
82 image_offset += pair.offset.to_usize();
83 }
84 5 => {
85 let image_size: u32 = s.read()?;
86 s.advance(8); let num_glyphs: u32 = s.read()?;
88 let glyphs = s.read_array32::<GlyphId>(num_glyphs)?;
89 let (index, _) = glyphs.binary_search(&glyph_id)?;
90 image_offset = image_offset
91 .checked_add(usize::num_from(index).checked_mul(usize::num_from(image_size))?)?;
92 }
93 _ => return None, }
95
96 Some(Location {
97 format: image_format,
98 offset: image_offset,
99 metrics,
100 ppem: size_table.ppem,
101 })
102}
103
104
105#[derive(Clone, Copy)]
106struct BitmapSizeTable {
107 subtable_array_offset: Offset32,
108 number_of_subtables: u32,
109 ppem: u16,
110 }
112
113fn select_bitmap_size_table(
114 glyph_id: GlyphId,
115 pixels_per_em: u16,
116 mut s: Stream,
117) -> Option<BitmapSizeTable> {
118 let subtable_count: u32 = s.read()?;
119 let orig_s = s.clone();
120
121 let mut idx = None;
122 let mut max_ppem = 0;
123 for i in 0..subtable_count {
124 s.advance(40); let start_glyph_id: GlyphId = s.read()?;
129 let end_glyph_id: GlyphId = s.read()?;
130 let ppem = u16::from(s.read::<u8>()?);
131
132 if !(start_glyph_id..=end_glyph_id).contains(&glyph_id) {
133 s.advance(4); continue;
135 }
136
137 if (pixels_per_em <= ppem && ppem < max_ppem) || (pixels_per_em > max_ppem && ppem > max_ppem) {
139 idx = Some(usize::num_from(i));
140 max_ppem = ppem;
141 }
142 }
143
144 let mut s = orig_s;
145 s.advance(idx? * 48); let subtable_array_offset: Offset32 = s.read()?;
148 s.skip::<u32>(); let number_of_subtables: u32 = s.read()?;
150
151 Some(BitmapSizeTable {
152 subtable_array_offset,
153 number_of_subtables,
154 ppem: max_ppem,
155 })
156}
157
158
159#[derive(Clone, Copy)]
160struct IndexSubtableInfo {
161 start_glyph_id: GlyphId,
162 offset: usize, }
164
165fn select_index_subtable(
166 data: &[u8],
167 size_table: BitmapSizeTable,
168 glyph_id: GlyphId,
169) -> Option<IndexSubtableInfo> {
170 let mut s = Stream::new_at(data, size_table.subtable_array_offset.to_usize())?;
171 for _ in 0..size_table.number_of_subtables {
172 let start_glyph_id: GlyphId = s.read()?;
173 let end_glyph_id: GlyphId = s.read()?;
174 let offset: Offset32 = s.read()?;
175
176 if (start_glyph_id..=end_glyph_id).contains(&glyph_id) {
177 let offset = size_table.subtable_array_offset.to_usize() + offset.to_usize();
178 return Some(IndexSubtableInfo {
179 start_glyph_id,
180 offset,
181 })
182 }
183 }
184
185 None
186}
187
188
189#[derive(Clone, Copy)]
190pub struct GlyphIdOffsetPair {
191 glyph_id: GlyphId,
192 offset: Offset16,
193}
194
195impl FromData for GlyphIdOffsetPair {
196 const SIZE: usize = 4;
197
198 #[inline]
199 fn parse(data: &[u8]) -> Option<Self> {
200 let mut s = Stream::new(data);
201 Some(GlyphIdOffsetPair {
202 glyph_id: s.read::<GlyphId>()?,
203 offset: s.read::<Offset16>()?,
204 })
205 }
206}