ttf_parser/tables/
hmtx.rsuse core::num::NonZeroU16;
use crate::GlyphId;
use crate::parser::{Stream, FromData, LazyArray16};
#[derive(Clone, Copy)]
struct HorizontalMetrics {
advance_width: u16,
lsb: i16,
}
impl FromData for HorizontalMetrics {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(HorizontalMetrics {
advance_width: s.read::<u16>()?,
lsb: s.read::<i16>()?,
})
}
}
#[derive(Clone, Copy)]
pub struct Table<'a> {
metrics: LazyArray16<'a, HorizontalMetrics>,
bearings: Option<LazyArray16<'a, i16>>,
number_of_metrics: u16, }
impl<'a> Table<'a> {
pub fn parse(
data: &'a [u8],
number_of_hmetrics: NonZeroU16,
number_of_glyphs: NonZeroU16,
) -> Option<Self> {
let mut s = Stream::new(data);
let metrics = s.read_array16::<HorizontalMetrics>(number_of_hmetrics.get())?;
let mut number_of_metrics = number_of_hmetrics.get();
let bearings_count = number_of_glyphs.get().checked_sub(number_of_hmetrics.get());
let bearings = if let Some(count) = bearings_count {
number_of_metrics += count;
s.read_array16::<i16>(count)
} else {
None
};
Some(Table {
metrics,
bearings,
number_of_metrics,
})
}
#[inline]
pub fn advance(&self, glyph_id: GlyphId) -> Option<u16> {
if glyph_id.0 >= self.number_of_metrics {
return None;
}
if let Some(metrics) = self.metrics.get(glyph_id.0) {
Some(metrics.advance_width)
} else {
self.metrics.last().map(|m| m.advance_width)
}
}
#[inline]
pub fn side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
if let Some(metrics) = self.metrics.get(glyph_id.0) {
Some(metrics.lsb)
} else if let Some(bearings) = self.bearings {
let number_of_hmetrics = self.metrics.len();
if glyph_id.0 < number_of_hmetrics {
return None;
}
bearings.get(glyph_id.0 - number_of_hmetrics)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! nzu16 {
($n:expr) => { NonZeroU16::new($n).unwrap() };
}
#[test]
fn simple_case() {
let data = &[
0x00, 0x01, 0x00, 0x02, ];
let table = Table::parse(data, nzu16!(1), nzu16!(1)).unwrap();
assert_eq!(table.advance(GlyphId(0)), Some(1));
assert_eq!(table.side_bearing(GlyphId(0)), Some(2));
}
#[test]
fn empty() {
assert!(Table::parse(&[], nzu16!(1), nzu16!(1)).is_none());
}
#[test]
fn smaller_than_glyphs_count() {
let data = &[
0x00, 0x01, 0x00, 0x02, 0x00, 0x03, ];
let table = Table::parse(data, nzu16!(1), nzu16!(2)).unwrap();
assert_eq!(table.advance(GlyphId(0)), Some(1));
assert_eq!(table.side_bearing(GlyphId(0)), Some(2));
assert_eq!(table.advance(GlyphId(1)), Some(1));
assert_eq!(table.side_bearing(GlyphId(1)), Some(3));
}
#[test]
fn less_metrics_than_glyphs() {
let data = &[
0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, ];
let table = Table::parse(data, nzu16!(2), nzu16!(1)).unwrap();
assert_eq!(table.side_bearing(GlyphId(0)), Some(2));
assert_eq!(table.side_bearing(GlyphId(1)), Some(4));
assert_eq!(table.side_bearing(GlyphId(2)), None);
}
#[test]
fn glyph_out_of_bounds_0() {
let data = &[
0x00, 0x01, 0x00, 0x02, ];
let table = Table::parse(data, nzu16!(1), nzu16!(1)).unwrap();
assert_eq!(table.advance(GlyphId(0)), Some(1));
assert_eq!(table.side_bearing(GlyphId(0)), Some(2));
assert_eq!(table.advance(GlyphId(1)), None);
assert_eq!(table.side_bearing(GlyphId(1)), None);
}
#[test]
fn glyph_out_of_bounds_1() {
let data = &[
0x00, 0x01, 0x00, 0x02, 0x00, 0x03, ];
let table = Table::parse(data, nzu16!(1), nzu16!(2)).unwrap();
assert_eq!(table.advance(GlyphId(1)), Some(1));
assert_eq!(table.side_bearing(GlyphId(1)), Some(3));
assert_eq!(table.advance(GlyphId(2)), None);
assert_eq!(table.side_bearing(GlyphId(2)), None);
}
}