1use super::{super::once::Once, FontBlob, FontSource};
4use crate::{
5 tables, types::Tag, FontRead, FontReadWithArgs, ReadError, TableProvider, TopLevelTable,
6};
7use alloc::{boxed::Box, sync::Arc};
8use core::sync::atomic::{AtomicU8, Ordering};
9
10include!("../../../data/generated/generated_tables.rs");
11
12#[derive(Default)]
14struct BlobTableEntry {
15 flag: AtomicU8,
17 start: u32,
18 end: u32,
19}
20
21struct BlobTables {
23 blob: FontBlob,
24 tables: Box<PerTableData<BlobTableEntry>>,
25}
26
27impl<'a> TableDataProvider<'a> for &'a BlobTables {
28 type Entry = BlobTableEntry;
29
30 fn tables(&self) -> &'a PerTableData<Self::Entry> {
31 &self.tables
32 }
33
34 fn table_state(&self, _tag: Tag, entry: &'a Self::Entry) -> Option<TableState<'a>> {
35 self.blob
36 .get(entry.start as usize..entry.end as usize)
37 .map(|data| TableState {
38 flag: &entry.flag,
39 data,
40 })
41 }
42}
43
44type TableFunctionEntry = Once<Option<(AtomicU8, FontBlob)>>;
46
47#[derive(Clone)]
49pub struct FontTableFunction {
50 table_fn: Arc<dyn Fn(Tag) -> Option<FontBlob> + Send + Sync>,
51 tables: Arc<PerTableData<TableFunctionEntry>>,
52}
53
54impl FontTableFunction {
55 pub fn new(table_fn: Arc<dyn Fn(Tag) -> Option<FontBlob> + Send + Sync>) -> Self {
58 Self {
59 table_fn,
60 tables: Arc::new(PerTableData::default()),
61 }
62 }
63}
64
65impl<'a> TableDataProvider<'a> for &'a FontTableFunction {
66 type Entry = TableFunctionEntry;
67
68 fn tables(&self) -> &'a PerTableData<Self::Entry> {
69 &self.tables
70 }
71
72 fn table_state(&self, tag: Tag, entry: &'a Self::Entry) -> Option<TableState<'a>> {
73 entry
74 .get_or_init(|| (self.table_fn)(tag).map(|blob| (AtomicU8::new(0), blob)))
75 .as_ref()
76 .map(|(flag, data)| TableState {
77 flag,
78 data: data.as_ref(),
79 })
80 }
81}
82
83enum TableSource {
85 None,
86 Blob(BlobTables),
87 Function(FontTableFunction),
88}
89
90#[derive(Copy, Clone)]
92struct TableState<'a> {
93 flag: &'a AtomicU8,
94 data: &'a [u8],
95}
96
97impl<'a> TableState<'a> {
98 const UNCHECKED: u8 = 0;
99 const VALID: u8 = 1;
100 const INVALID: u8 = 2;
101
102 fn try_load<T>(
103 &self,
104 sanitize_fn: impl Fn(&'a [u8]) -> Result<T, ReadError>,
105 load_fn: impl Fn(&'a [u8]) -> Result<T, ReadError>,
106 ) -> Result<T, ReadError> {
107 match self.flag.load(Ordering::Acquire) {
108 Self::UNCHECKED => {
109 if let Ok(table) = sanitize_fn(self.data) {
110 self.flag.store(Self::VALID, Ordering::Release);
111 Ok(table)
112 } else {
113 self.flag.store(Self::INVALID, Ordering::Release);
114 Err(ReadError::ValidationError)
115 }
116 }
117 Self::VALID => load_fn(self.data),
118 _ => Err(ReadError::ValidationError),
120 }
121 }
122}
123
124pub struct FontTables(TableSource);
126
127impl FontTables {
128 pub fn new(source: impl Into<FontSource>, index: u32) -> Result<Self, ReadError> {
130 let source = source.into();
131 match source {
132 FontSource::Blob(blob) => {
133 let font_ref = crate::FontRef::from_index(&blob, index)?;
134 let mut tables = PerTableData::default();
135 let table_records = font_ref.table_directory().table_records();
136 tables.init_all(|tag, entry: &mut BlobTableEntry| {
137 entry.start = u32::MAX;
140 let Ok(idx) = table_records.binary_search_by_key(&tag, |rec| rec.tag()) else {
141 return;
142 };
143 let record = &table_records[idx];
144 let start = record.offset();
145 let Some(end) = start.checked_add(record.length()) else {
146 return;
147 };
148 if blob.get(start as usize..end as usize).is_none() {
149 return;
150 }
151 *entry = BlobTableEntry {
152 flag: AtomicU8::new(0),
153 start,
154 end,
155 };
156 });
157 Ok(Self(TableSource::Blob(BlobTables {
158 blob,
159 tables: Box::new(tables),
160 })))
161 }
162 FontSource::TableFunction(func) => Ok(Self(TableSource::Function(func))),
163 }
164 }
165}
166
167impl FontTables {
168 fn load_table<'a, T: TopLevelTable + FontRead<'a>>(
169 &'a self,
170 state: Option<TableState<'a>>,
171 ) -> Result<T, ReadError> {
172 self.load_table_with_tag(state, T::TAG)
173 }
174
175 fn load_table_with_tag<'a, T: FontRead<'a>>(
176 &'a self,
177 state: Option<TableState<'a>>,
178 tag: Tag,
179 ) -> Result<T, ReadError> {
180 let state = state.ok_or(ReadError::TableIsMissing(tag))?;
181 state.try_load(
182 |data| FontRead::read(data.into()),
183 |data| FontRead::read(data.into()),
184 )
185 }
186
187 fn load_table_with_args<'a, T: TopLevelTable + FontReadWithArgs<'a>>(
188 &'a self,
189 state: Option<TableState<'a>>,
190 args: &T::Args,
191 ) -> Result<T, ReadError> {
192 let state = state.ok_or(ReadError::TableIsMissing(T::TAG))?;
193 state.try_load(
194 |data| FontReadWithArgs::read_with_args(data.into(), args),
195 |data| FontReadWithArgs::read_with_args(data.into(), args),
196 )
197 }
198}
199
200pub(super) static EMPTY_FONT_TABLES: FontTables = FontTables(TableSource::None);
201
202impl<'a> TableProvider<'a> for &'a FontTables {
203 fn data_for_tag(&self, _tag: Tag) -> Option<crate::FontData<'a>> {
204 None
205 }
206
207 fn head(&self) -> Result<tables::head::Head<'a>, ReadError> {
208 self.load_table(self.head_state())
209 }
210
211 fn name(&self) -> Result<tables::name::Name<'a>, ReadError> {
212 self.load_table(self.name_state())
213 }
214
215 fn hhea(&self) -> Result<tables::hhea::Hhea<'a>, ReadError> {
216 self.load_table(self.hhea_state())
217 }
218
219 fn vhea(&self) -> Result<tables::vhea::Vhea<'a>, ReadError> {
220 self.load_table(self.vhea_state())
221 }
222
223 fn hmtx(&self) -> Result<tables::hmtx::Hmtx<'a>, ReadError> {
224 let number_of_h_metrics = self.hhea().map(|hhea| hhea.number_of_h_metrics())?;
226 self.load_table_with_args(self.hmtx_state(), &number_of_h_metrics)
227 }
228
229 fn hdmx(&self) -> Result<tables::hdmx::Hdmx<'a>, ReadError> {
230 let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?;
231 self.load_table_with_args(self.hdmx_state(), &num_glyphs)
232 }
233
234 fn vmtx(&self) -> Result<tables::vmtx::Vmtx<'a>, ReadError> {
235 let number_of_v_metrics = self.vhea().map(|vhea| vhea.number_of_long_ver_metrics())?;
237 self.load_table_with_args(self.vmtx_state(), &number_of_v_metrics)
238 }
239
240 fn vorg(&self) -> Result<tables::vorg::Vorg<'a>, ReadError> {
241 self.load_table(self.vorg_state())
242 }
243
244 fn fvar(&self) -> Result<tables::fvar::Fvar<'a>, ReadError> {
245 self.load_table(self.fvar_state())
246 }
247
248 fn avar(&self) -> Result<tables::avar::Avar<'a>, ReadError> {
249 self.load_table(self.avar_state())
250 }
251
252 fn hvar(&self) -> Result<tables::hvar::Hvar<'a>, ReadError> {
253 self.load_table(self.hvar_state())
254 }
255
256 fn vvar(&self) -> Result<tables::vvar::Vvar<'a>, ReadError> {
257 self.load_table(self.vvar_state())
258 }
259
260 fn mvar(&self) -> Result<tables::mvar::Mvar<'a>, ReadError> {
261 self.load_table(self.mvar_state())
262 }
263
264 fn maxp(&self) -> Result<tables::maxp::Maxp<'a>, ReadError> {
265 self.load_table(self.maxp_state())
266 }
267
268 fn os2(&self) -> Result<tables::os2::Os2<'a>, ReadError> {
269 self.load_table(self.os2_state())
270 }
271
272 fn post(&self) -> Result<tables::post::Post<'a>, ReadError> {
273 self.load_table(self.post_state())
274 }
275
276 fn gasp(&self) -> Result<tables::gasp::Gasp<'a>, ReadError> {
277 self.load_table(self.gasp_state())
278 }
279
280 fn loca(&self, is_long: impl Into<Option<bool>>) -> Result<tables::loca::Loca<'a>, ReadError> {
282 let is_long = match is_long.into() {
283 Some(val) => val,
284 None => self.head()?.index_to_loc_format() == 1,
285 };
286 self.load_table_with_args(self.loca_state(), &is_long)
287 }
288
289 fn glyf(&self) -> Result<tables::glyf::Glyf<'a>, ReadError> {
290 self.load_table(self.glyf_state())
291 }
292
293 fn gvar(&self) -> Result<tables::gvar::Gvar<'a>, ReadError> {
294 self.load_table(self.gvar_state())
295 }
296
297 fn cvt(&self) -> Result<&'a [types::BigEndian<i16>], ReadError> {
300 let table_data = crate::FontData::new(
301 self.cvt_data()
302 .ok_or(ReadError::TableIsMissing(Tag::new(b"cvt ")))?,
303 );
304 table_data.read_array(0..table_data.len())
305 }
306
307 fn cvar(&self) -> Result<tables::cvar::Cvar<'a>, ReadError> {
308 self.load_table(self.cvar_state())
309 }
310
311 fn cff(&self) -> Result<tables::cff::Cff<'a>, ReadError> {
312 self.load_table(self.cff_state())
313 }
314
315 fn cff2(&self) -> Result<tables::cff2::Cff2<'a>, ReadError> {
316 self.load_table(self.cff2_state())
317 }
318
319 fn cmap(&self) -> Result<tables::cmap::Cmap<'a>, ReadError> {
320 self.load_table(self.cmap_state())
321 }
322
323 fn gdef(&self) -> Result<tables::gdef::Gdef<'a>, ReadError> {
324 self.load_table(self.gdef_state())
325 }
326
327 fn gpos(&self) -> Result<tables::gpos::Gpos<'a>, ReadError> {
328 self.load_table(self.gpos_state())
329 }
330
331 fn gsub(&self) -> Result<tables::gsub::Gsub<'a>, ReadError> {
332 self.load_table(self.gsub_state())
333 }
334
335 fn feat(&self) -> Result<tables::feat::Feat<'a>, ReadError> {
336 self.load_table(self.feat_state())
337 }
338
339 fn ltag(&self) -> Result<tables::ltag::Ltag<'a>, ReadError> {
340 self.load_table(self.ltag_state())
341 }
342
343 fn ankr(&self) -> Result<tables::ankr::Ankr<'a>, ReadError> {
344 self.load_table(self.ankr_state())
345 }
346
347 fn trak(&self) -> Result<tables::trak::Trak<'a>, ReadError> {
348 self.load_table(self.trak_state())
349 }
350
351 fn morx(&self) -> Result<tables::morx::Morx<'a>, ReadError> {
352 self.load_table(self.morx_state())
353 }
354
355 fn kerx(&self) -> Result<tables::kerx::Kerx<'a>, ReadError> {
356 self.load_table(self.kerx_state())
357 }
358
359 fn kern(&self) -> Result<tables::kern::Kern<'a>, ReadError> {
360 self.load_table(self.kern_state())
361 }
362
363 fn colr(&self) -> Result<tables::colr::Colr<'a>, ReadError> {
364 self.load_table(self.colr_state())
365 }
366
367 fn cpal(&self) -> Result<tables::cpal::Cpal<'a>, ReadError> {
368 self.load_table(self.cpal_state())
369 }
370
371 fn cblc(&self) -> Result<tables::cblc::Cblc<'a>, ReadError> {
372 self.load_table(self.cblc_state())
373 }
374
375 fn cbdt(&self) -> Result<tables::cbdt::Cbdt<'a>, ReadError> {
376 self.load_table(self.cbdt_state())
377 }
378
379 fn eblc(&self) -> Result<tables::eblc::Eblc<'a>, ReadError> {
380 self.load_table(self.eblc_state())
381 }
382
383 fn ebdt(&self) -> Result<tables::ebdt::Ebdt<'a>, ReadError> {
384 self.load_table(self.ebdt_state())
385 }
386
387 fn sbix(&self) -> Result<tables::sbix::Sbix<'a>, ReadError> {
388 let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?;
390 self.load_table_with_args(self.sbix_state(), &num_glyphs)
391 }
392
393 fn stat(&self) -> Result<tables::stat::Stat<'a>, ReadError> {
394 self.load_table(self.stat_state())
395 }
396
397 fn svg(&self) -> Result<tables::svg::Svg<'a>, ReadError> {
398 self.load_table(self.svg_state())
399 }
400
401 fn varc(&self) -> Result<tables::varc::Varc<'a>, ReadError> {
402 self.load_table(self.varc_state())
403 }
404
405 #[cfg(feature = "ift")]
406 fn ift(&self) -> Result<tables::ift::Ift<'a>, ReadError> {
407 self.load_table_with_tag(self.ift_state(), Tag::new(b"IFT "))
408 }
409
410 #[cfg(feature = "ift")]
411 fn iftx(&self) -> Result<tables::ift::Ift<'a>, ReadError> {
412 self.load_table_with_tag(self.iftx_state(), Tag::new(b"IFTX"))
413 }
414
415 fn meta(&self) -> Result<tables::meta::Meta<'a>, ReadError> {
416 self.load_table(self.meta_state())
417 }
418
419 fn base(&self) -> Result<tables::base::Base<'a>, ReadError> {
420 self.load_table(self.base_state())
421 }
422
423 fn dsig(&self) -> Result<tables::dsig::Dsig<'a>, ReadError> {
424 self.load_table(self.dsig_state())
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use crate::FontRef;
431
432 use super::*;
433
434 #[test]
435 fn missing_tables_are_missing() {
436 let tables = &FontTables::new(font_test_data::AHEM, 0).unwrap();
437 assert!(matches!(tables.cbdt(), Err(ReadError::TableIsMissing(_))));
438 assert!(matches!(tables.cff(), Err(ReadError::TableIsMissing(_))));
439 assert!(matches!(tables.dsig(), Err(ReadError::TableIsMissing(_))));
440 }
441
442 #[test]
443 fn table_function_matches_font_ref() {
444 let font = FontRef::new(font_test_data::AHEM).unwrap();
445 let font_copy = font.clone();
446 let table_fn = FontTableFunction::new(Arc::new(move |tag| {
447 font_copy
448 .data_for_tag(tag)
449 .map(|data| Vec::from(data.as_bytes()).into())
450 }));
451 let tables = &FontTables::new(table_fn, 0).unwrap();
452 for (data, tag) in [
453 (tables.gasp_data(), b"gasp"),
454 (tables.glyf_data(), b"glyf"),
455 (tables.hmtx_data(), b"hmtx"),
456 ] {
457 let font_ref_data = font.data_for_tag(Tag::new(tag)).map(|d| d.as_bytes());
458 assert_eq!(data, font_ref_data);
459 }
460 }
461}