use crate::parser::{read_i16, read_u16};
use crate::Error;
#[derive(Debug, Clone, Copy)]
pub struct HheaTable {
pub ascent: i16,
pub descent: i16,
pub line_gap: i16,
pub advance_width_max: u16,
pub num_long_hor_metrics: u16,
}
impl HheaTable {
pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < 36 {
return Err(Error::UnexpectedEof);
}
let ascent = read_i16(bytes, 4)?;
let descent = read_i16(bytes, 6)?;
let line_gap = read_i16(bytes, 8)?;
let advance_width_max = read_u16(bytes, 10)?;
let num_long_hor_metrics = read_u16(bytes, 34)?;
if num_long_hor_metrics == 0 {
return Err(Error::BadStructure("hhea.numberOfHMetrics == 0"));
}
Ok(Self {
ascent,
descent,
line_gap,
advance_width_max,
num_long_hor_metrics,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_minimal() {
let mut b = vec![0u8; 36];
b[0..4].copy_from_slice(&0x00010000u32.to_be_bytes());
b[4..6].copy_from_slice(&(1900i16).to_be_bytes());
b[6..8].copy_from_slice(&(-500i16).to_be_bytes());
b[8..10].copy_from_slice(&(0i16).to_be_bytes());
b[10..12].copy_from_slice(&(2048u16).to_be_bytes());
b[34..36].copy_from_slice(&(1u16).to_be_bytes());
let h = HheaTable::parse(&b).unwrap();
assert_eq!(h.ascent, 1900);
assert_eq!(h.descent, -500);
assert_eq!(h.advance_width_max, 2048);
assert_eq!(h.num_long_hor_metrics, 1);
}
#[test]
fn rejects_zero_metrics() {
let mut b = vec![0u8; 36];
b[0..4].copy_from_slice(&0x00010000u32.to_be_bytes());
assert!(HheaTable::parse(&b).is_err());
}
}