//! a trait for things that can serve font tables use types::{BigEndian, Tag}; use crate::{tables, FontData, FontRead, ReadError}; /// A table that has an associated tag. /// /// This is true of top-level tables, but not their various subtables. pub trait TopLevelTable { /// The table's tag. const TAG: Tag; } /// An interface for accessing tables from a font (or font-like object) pub trait TableProvider<'a> { fn data_for_tag(&self, tag: Tag) -> Option>; fn expect_data_for_tag(&self, tag: Tag) -> Result, ReadError> { self.data_for_tag(tag).ok_or(ReadError::TableIsMissing(tag)) } fn expect_table>(&self) -> Result { self.expect_data_for_tag(T::TAG).and_then(FontRead::read) } fn head(&self) -> Result, ReadError> { self.expect_table() } fn name(&self) -> Result, ReadError> { self.expect_table() } fn hhea(&self) -> Result, ReadError> { self.expect_table() } fn vhea(&self) -> Result, ReadError> { self.expect_table() } fn hmtx(&self) -> Result, ReadError> { //FIXME: should we make the user pass these in? let number_of_h_metrics = self.hhea().map(|hhea| hhea.number_of_h_metrics())?; let data = self.expect_data_for_tag(tables::hmtx::Hmtx::TAG)?; tables::hmtx::Hmtx::read(data, number_of_h_metrics) } fn hdmx(&self) -> Result, ReadError> { let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?; let data = self.expect_data_for_tag(tables::hdmx::Hdmx::TAG)?; tables::hdmx::Hdmx::read(data, num_glyphs) } fn vmtx(&self) -> Result, ReadError> { //FIXME: should we make the user pass these in? let number_of_v_metrics = self.vhea().map(|vhea| vhea.number_of_long_ver_metrics())?; let data = self.expect_data_for_tag(tables::vmtx::Vmtx::TAG)?; tables::vmtx::Vmtx::read(data, number_of_v_metrics) } fn vorg(&self) -> Result, ReadError> { self.expect_table() } fn fvar(&self) -> Result, ReadError> { self.expect_table() } fn avar(&self) -> Result, ReadError> { self.expect_table() } fn hvar(&self) -> Result, ReadError> { self.expect_table() } fn vvar(&self) -> Result, ReadError> { self.expect_table() } fn mvar(&self) -> Result, ReadError> { self.expect_table() } fn maxp(&self) -> Result, ReadError> { self.expect_table() } fn os2(&self) -> Result, ReadError> { self.expect_table() } fn post(&self) -> Result, ReadError> { self.expect_table() } fn gasp(&self) -> Result, ReadError> { self.expect_table() } /// is_long can be optionally provided, if known, otherwise we look it up in head. fn loca(&self, is_long: impl Into>) -> Result, ReadError> { let is_long = match is_long.into() { Some(val) => val, None => self.head()?.index_to_loc_format() == 1, }; let data = self.expect_data_for_tag(tables::loca::Loca::TAG)?; tables::loca::Loca::read(data, is_long) } fn glyf(&self) -> Result, ReadError> { self.expect_table() } fn gvar(&self) -> Result, ReadError> { self.expect_table() } /// Returns the array of entries for the control value table which is used /// for TrueType hinting. fn cvt(&self) -> Result<&'a [BigEndian], ReadError> { let table_data = self.expect_data_for_tag(Tag::new(b"cvt "))?; table_data.read_array(0..table_data.len()) } fn cvar(&self) -> Result, ReadError> { self.expect_table() } fn cff(&self) -> Result, ReadError> { self.expect_table() } fn cff2(&self) -> Result, ReadError> { self.expect_table() } fn cmap(&self) -> Result, ReadError> { self.expect_table() } fn gdef(&self) -> Result, ReadError> { self.expect_table() } fn gpos(&self) -> Result, ReadError> { self.expect_table() } fn gsub(&self) -> Result, ReadError> { self.expect_table() } fn feat(&self) -> Result, ReadError> { self.expect_table() } fn ltag(&self) -> Result, ReadError> { self.expect_table() } fn ankr(&self) -> Result, ReadError> { self.expect_table() } fn trak(&self) -> Result, ReadError> { self.expect_table() } fn morx(&self) -> Result, ReadError> { self.expect_table() } fn kerx(&self) -> Result, ReadError> { self.expect_table() } fn kern(&self) -> Result, ReadError> { self.expect_table() } fn colr(&self) -> Result, ReadError> { self.expect_table() } fn cpal(&self) -> Result, ReadError> { self.expect_table() } fn cblc(&self) -> Result, ReadError> { self.expect_table() } fn cbdt(&self) -> Result, ReadError> { self.expect_table() } fn eblc(&self) -> Result, ReadError> { self.expect_table() } fn ebdt(&self) -> Result, ReadError> { self.expect_table() } fn sbix(&self) -> Result, ReadError> { // should we make the user pass this in? let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?; let data = self.expect_data_for_tag(tables::sbix::Sbix::TAG)?; tables::sbix::Sbix::read(data, num_glyphs) } fn stat(&self) -> Result, ReadError> { self.expect_table() } fn svg(&self) -> Result, ReadError> { self.expect_table() } fn varc(&self) -> Result, ReadError> { self.expect_table() } #[cfg(feature = "ift")] fn ift(&self) -> Result, ReadError> { self.expect_data_for_tag(tables::ift::IFT_TAG) .and_then(FontRead::read) } #[cfg(feature = "ift")] fn iftx(&self) -> Result, ReadError> { self.expect_data_for_tag(tables::ift::IFTX_TAG) .and_then(FontRead::read) } fn meta(&self) -> Result, ReadError> { self.expect_table() } fn base(&self) -> Result, ReadError> { self.expect_table() } fn dsig(&self) -> Result, ReadError> { self.expect_table() } } #[cfg(test)] mod tests { use super::*; /// https://github.com/googlefonts/fontations/issues/105 #[test] fn bug_105() { // serve some dummy versions of the tables used to compute hmtx. The only // fields that matter are maxp::num_glyphs and hhea::number_of_h_metrics, // everything else is zero'd out struct DummyProvider; impl TableProvider<'static> for DummyProvider { fn data_for_tag(&self, tag: Tag) -> Option> { if tag == Tag::new(b"maxp") { Some(FontData::new(&[ 0, 0, 0x50, 0, // version 0.5 0, 3, // num_glyphs = 3 ])) } else if tag == Tag::new(b"hhea") { Some(FontData::new(&[ 0, 1, 0, 0, // version 1.0 0, 0, 0, 0, // ascender/descender 0, 0, 0, 0, // line gap/advance width 0, 0, 0, 0, // min left/right side bearing 0, 0, 0, 0, // x_max, caret_slope_rise 0, 0, 0, 0, // caret_slope_run, caret_offset 0, 0, 0, 0, // reserved1/2 0, 0, 0, 0, // reserved 3/4 0, 0, 0, 1, // metric format, number_of_h_metrics ])) } else if tag == Tag::new(b"hmtx") { Some(FontData::new(&[ 0, 4, 0, 6, // LongHorMetric: 4, 6 0, 30, 0, 111, // two lsb entries ])) } else { None } } } let number_of_h_metrics = DummyProvider.hhea().unwrap().number_of_h_metrics(); let num_glyphs = DummyProvider.maxp().unwrap().num_glyphs(); let hmtx = DummyProvider.hmtx().unwrap(); assert_eq!(number_of_h_metrics, 1); assert_eq!(num_glyphs, 3); assert_eq!(hmtx.h_metrics().len(), 1); assert_eq!(hmtx.left_side_bearings().len(), 2); } }