use super::*;
#[derive(Clone)]
pub enum LayoutOffsetArray<'a, T: FontRead<'a, Args = ()>> {
Offset16(ArrayOfOffsets<'a, T, Offset16>),
Offset24(ArrayOfOffsets<'a, T, Offset24>),
Offset32(ArrayOfOffsets<'a, T, Offset32>),
}
impl<'a, T: FontRead<'a, Args = ()> + 'a> LayoutOffsetArray<'a, T> {
pub fn len(&self) -> usize {
match self {
Self::Offset16(array) => array.len(),
Self::Offset24(array) => array.len(),
Self::Offset32(array) => array.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Result<T, ReadError> {
match self {
Self::Offset16(array) => array.get(index),
Self::Offset24(array) => array.get(index),
Self::Offset32(array) => array.get(index),
}
}
pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
let (one, two, three) = match self {
Self::Offset16(array) => (Some(array.iter()), None, None),
Self::Offset24(array) => (None, Some(array.iter()), None),
Self::Offset32(array) => (None, None, Some(array.iter())),
};
one.into_iter()
.flatten()
.chain(two.into_iter().flatten())
.chain(three.into_iter().flatten())
}
pub fn iter_as_nullable(&self) -> impl Iterator<Item = Option<Result<T, ReadError>>> + 'a {
self.iter().map(|result| match result {
Err(ReadError::NullOffset) => None,
other => Some(other),
})
}
}
#[derive(Clone)]
pub enum SelectedLookupList<'a, T: FontRead<'a, Args = ()>> {
Original(LookupList<'a, T>),
Extended(LookupList2<'a, T>),
}
impl<'a, T: FontRead<'a, Args = ()>> From<LookupList<'a, T>> for SelectedLookupList<'a, T> {
fn from(list: LookupList<'a, T>) -> Self {
Self::Original(list)
}
}
impl<'a, T: FontRead<'a, Args = ()>> From<LookupList2<'a, T>> for SelectedLookupList<'a, T> {
fn from(list: LookupList2<'a, T>) -> Self {
Self::Extended(list)
}
}
impl<'a, T: FontRead<'a, Args = ()> + 'a> SelectedLookupList<'a, T> {
pub fn lookup_count(&self) -> u16 {
match self {
Self::Original(table) => table.lookup_count(),
Self::Extended(table) => table.lookup_count(),
}
}
pub fn lookups(&self) -> LayoutOffsetArray<'a, T> {
match self {
Self::Original(table) => LayoutOffsetArray::Offset16(table.lookups()),
Self::Extended(table) => LayoutOffsetArray::Offset32(table.lookups()),
}
}
pub fn offset_data(&self) -> FontData<'a> {
match self {
Self::Original(table) => table.offset_data(),
Self::Extended(table) => table.offset_data(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum HeaderOffset {
Original(Offset32),
Extended(Offset32),
}
impl HeaderOffset {
pub(crate) fn select(
original: Nullable<Offset16>,
extended: Option<Nullable<Offset32>>,
has_extended: bool,
) -> Result<Self, ReadError> {
if has_extended {
let extended = *extended.ok_or(ReadError::OutOfBounds)?.offset();
if !extended.is_null() {
return Ok(Self::Extended(extended));
}
}
Ok(Self::Original(Offset32::new(original.offset().to_u32())))
}
pub(crate) fn offset(self) -> Offset32 {
match self {
Self::Original(offset) | Self::Extended(offset) => offset,
}
}
pub(crate) fn is_null(self) -> bool {
self.offset().is_null()
}
pub(crate) fn resolve<'a, T: FontRead<'a, Args = ()>>(
self,
data: FontData<'a>,
) -> Option<Result<T, ReadError>> {
(!self.is_null()).then(|| self.offset().resolve(data))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn original(offset: u16) -> Nullable<Offset16> {
Scalar::from_raw(offset.to_be_bytes())
}
fn extended(offset: u32) -> Option<Nullable<Offset32>> {
Some(Scalar::from_raw(offset.to_be_bytes()))
}
#[test]
fn header_offset_prefers_nonzero_extended_offset() {
assert_eq!(
HeaderOffset::select(original(10), extended(70000), true),
Ok(HeaderOffset::Extended(Offset32::new(70000)))
);
}
#[test]
fn header_offset_falls_back_to_original_offset() {
assert_eq!(
HeaderOffset::select(original(10), extended(0), true),
Ok(HeaderOffset::Original(Offset32::new(10)))
);
assert_eq!(
HeaderOffset::select(original(10), extended(70000), false),
Ok(HeaderOffset::Original(Offset32::new(10)))
);
assert!(HeaderOffset::select(original(0), extended(0), true)
.unwrap()
.is_null());
}
#[test]
fn header_offset_requires_extended_field_in_extended_header() {
assert_eq!(
HeaderOffset::select(original(10), None, true),
Err(ReadError::OutOfBounds)
);
assert_eq!(
HeaderOffset::select(original(10), None, false),
Ok(HeaderOffset::Original(Offset32::new(10)))
);
}
}