use core::convert::TryFrom;
use crate::parser::{Stream, FromData};
#[derive(Clone, Copy)]
struct SubHeaderRecord {
first_code: u16,
entry_count: u16,
id_delta: i16,
id_range_offset: u16,
}
impl FromData for SubHeaderRecord {
const SIZE: usize = 8;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(SubHeaderRecord {
first_code: s.read::<u16>()?,
entry_count: s.read::<u16>()?,
id_delta: s.read::<i16>()?,
id_range_offset: s.read::<u16>()?,
})
}
}
pub fn parse(data: &[u8], code_point: u32) -> Option<u16> {
let code_point = u16::try_from(code_point).ok()?;
let code_point = code_point;
let high_byte = code_point >> 8;
let low_byte = code_point & 0x00FF;
let mut s = Stream::new(data);
s.skip::<u16>(); s.skip::<u16>(); s.skip::<u16>(); let sub_header_keys = s.read_array16::<u16>(256)?;
let sub_headers_count = sub_header_keys.into_iter().map(|n| n / 8).max()? + 1;
let sub_headers_offset = s.offset();
let sub_headers = s.read_array16::<SubHeaderRecord>(sub_headers_count)?;
let i = if code_point < 0xff {
0
} else {
sub_header_keys.get(high_byte)? / 8
};
let sub_header = sub_headers.get(i)?;
let first_code = sub_header.first_code;
let range_end = first_code.checked_add(sub_header.entry_count)?;
if low_byte < first_code || low_byte >= range_end {
return None;
}
let index_offset = usize::from(low_byte.checked_sub(first_code)?) * u16::SIZE;
let offset =
sub_headers_offset
+ SubHeaderRecord::SIZE * usize::from(i + 1)
- u16::SIZE
+ usize::from(sub_header.id_range_offset)
+ index_offset;
let glyph: u16 = Stream::read_at(data, offset)?;
if glyph == 0 {
return None;
}
u16::try_from((i32::from(glyph) + i32::from(sub_header.id_delta)) % 65536).ok()
}
pub fn codepoints(data: &[u8], mut f: impl FnMut(u32)) -> Option<()> {
let mut s = Stream::new(data);
s.skip::<u16>(); s.skip::<u16>(); s.skip::<u16>(); let sub_header_keys = s.read_array16::<u16>(256)?;
let sub_headers_count = sub_header_keys.into_iter().map(|n| n / 8).max()? + 1;
let sub_headers = s.read_array16::<SubHeaderRecord>(sub_headers_count)?;
for first_byte in 0u16..256 {
let i = sub_header_keys.get(first_byte)? / 8;
let sub_header = sub_headers.get(i)?;
let first_code = sub_header.first_code;
if i == 0 {
let range_end = first_code.checked_add(sub_header.entry_count)?;
if first_byte >= first_code && first_byte < range_end {
f(u32::from(first_byte));
}
} else {
let base = first_code.checked_add(first_byte << 8)?;
for k in 0..sub_header.entry_count {
let code_point = base.checked_add(k)?;
f(u32::from(code_point));
}
}
}
Some(())
}
#[cfg(test)]
mod tests {
use crate::parser::FromData;
use super::{parse, codepoints};
#[test]
fn collect_codepoints() {
let mut data = vec![
0x00, 0x02, 0x02, 0x16, 0x00, 0x00, ];
data.extend(std::iter::repeat(0x00).take(256 * u16::SIZE));
data[6 + 0x28 * u16::SIZE + 1] = 0x08;
data.extend(&[
0x00, 0xFE, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, ]);
let mut vec = vec![];
codepoints(&data, |c| vec.push(c));
assert_eq!(vec, [10256, 10257, 10258, 254, 255]);
}
#[test]
fn codepoint_at_range_end() {
let mut data = vec![
0x00, 0x02, 0x02, 0x14, 0x00, 0x00, ];
data.extend(std::iter::repeat(0x00).take(256 * u16::SIZE));
data.extend(&[
0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x64, 0x03, 0xE8, 0x03, 0xE8, ]);
assert_eq!(parse(&data, 39), None);
assert_eq!(parse(&data, 40), Some(100));
assert_eq!(parse(&data, 41), Some(1000));
assert_eq!(parse(&data, 42), None);
}
}