use super::{Error, Index1, Index2};
use crate::codegen_prelude::*;
#[derive(Clone)]
pub enum Index<'a> {
Empty,
Format1(Index1<'a>),
Format2(Index2<'a>),
}
impl<'a> Index<'a> {
pub fn new(data: &'a [u8], is_cff2: bool) -> Result<Self, Error> {
let data = FontData::new(data);
Ok(if is_cff2 {
Index2::read(data).map(|ix| ix.into())?
} else {
Index1::read(data).map(|ix| ix.into())?
})
}
pub fn count(&self) -> u32 {
match self {
Self::Empty => 0,
Self::Format1(ix) => ix.count() as u32,
Self::Format2(ix) => ix.count(),
}
}
pub fn subr_bias(&self) -> i32 {
let count = self.count();
if count < 1240 {
107
} else if count < 33900 {
1131
} else {
32768
}
}
pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
match self {
Self::Empty => Ok(0),
Self::Format1(ix) => ix.size_in_bytes(),
Self::Format2(ix) => ix.size_in_bytes(),
}
}
pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
match self {
Self::Empty => Err(ReadError::OutOfBounds.into()),
Self::Format1(ix) => ix.get_offset(index),
Self::Format2(ix) => ix.get_offset(index),
}
}
pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
match self {
Self::Empty => Err(ReadError::OutOfBounds.into()),
Self::Format1(ix) => ix.get(index),
Self::Format2(ix) => ix.get(index),
}
}
}
impl<'a> From<Index1<'a>> for Index<'a> {
fn from(value: Index1<'a>) -> Self {
Self::Format1(value)
}
}
impl<'a> From<Index2<'a>> for Index<'a> {
fn from(value: Index2<'a>) -> Self {
Self::Format2(value)
}
}
impl Default for Index<'_> {
fn default() -> Self {
Self::Empty
}
}
impl<'a> Index1<'a> {
pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
const HEADER_SIZE: usize = 3;
const EMPTY_SIZE: usize = 2;
let count = self.count() as usize;
Ok(match count {
0 => EMPTY_SIZE,
_ => {
HEADER_SIZE
+ self.offsets().len()
+ self.get_offset(count).map_err(|_| ReadError::OutOfBounds)?
}
})
}
pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
read_offset(
index,
self.count() as usize,
self.off_size(),
self.offsets(),
)
}
pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
self.data()
.get(self.get_offset(index)?..self.get_offset(index + 1)?)
.ok_or(ReadError::OutOfBounds.into())
}
}
impl<'a> Index2<'a> {
pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
const HEADER_SIZE: usize = 5;
const EMPTY_SIZE: usize = 4;
let count = self.count() as usize;
Ok(match count {
0 => EMPTY_SIZE,
_ => {
HEADER_SIZE
+ self.offsets().len()
+ self.get_offset(count).map_err(|_| ReadError::OutOfBounds)?
}
})
}
pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
read_offset(
index,
self.count() as usize,
self.off_size(),
self.offsets(),
)
}
pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
self.data()
.get(self.get_offset(index)?..self.get_offset(index + 1)?)
.ok_or(ReadError::OutOfBounds.into())
}
}
fn read_offset(
index: usize,
count: usize,
offset_size: u8,
offset_data: &[u8],
) -> Result<usize, Error> {
if index > count {
Err(ReadError::OutOfBounds)?;
}
let data_offset = index * offset_size as usize;
let offset_data = FontData::new(offset_data);
match offset_size {
1 => offset_data.read_at::<u8>(data_offset)? as usize,
2 => offset_data.read_at::<u16>(data_offset)? as usize,
3 => offset_data.read_at::<Uint24>(data_offset)?.to_u32() as usize,
4 => offset_data.read_at::<u32>(data_offset)? as usize,
_ => return Err(Error::InvalidIndexOffsetSize(offset_size)),
}
.checked_sub(1)
.ok_or(Error::ZeroOffsetInIndex)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::BeBuffer;
enum IndexParams {
Format1 { off_size: u8, count: usize },
Format2 { off_size: u8, count: usize },
}
#[test]
fn index_format1_offsize1_count4() {
test_index(IndexParams::Format1 {
off_size: 1,
count: 4,
});
}
#[test]
fn index_format1_offsize2_count64() {
test_index(IndexParams::Format1 {
off_size: 2,
count: 64,
});
}
#[test]
fn index_format1_offsize3_count128() {
test_index(IndexParams::Format1 {
off_size: 3,
count: 128,
});
}
#[test]
fn index_format1_offsize4_count256() {
test_index(IndexParams::Format1 {
off_size: 4,
count: 256,
});
}
#[test]
fn index_format2_offsize1_count4() {
test_index(IndexParams::Format2 {
off_size: 4,
count: 256,
});
}
#[test]
fn index_format2_offsize2_count64() {
test_index(IndexParams::Format2 {
off_size: 2,
count: 64,
});
}
#[test]
fn index_format2_offsize3_count128() {
test_index(IndexParams::Format2 {
off_size: 3,
count: 128,
});
}
#[test]
fn index_format2_offsize4_count256() {
test_index(IndexParams::Format2 {
off_size: 4,
count: 256,
});
}
fn test_index(params: IndexParams) {
let (fmt, off_size, count) = match params {
IndexParams::Format1 { off_size, count } => (1, off_size, count),
IndexParams::Format2 { off_size, count } => (2, off_size, count),
};
let buf = make_index(fmt, off_size, count);
let index = Index::new(buf.font_data().as_bytes(), fmt == 2).unwrap();
let built_off_size = match &index {
Index::Empty => 0,
Index::Format1(v1) => v1.off_size(),
Index::Format2(v2) => v2.off_size(),
};
assert_eq!(built_off_size, off_size);
assert_eq!(index.count(), count as u32);
for i in 0..count {
let object = index.get(i).unwrap();
let expected_len = (i + 1) * 10;
let expected_bytes = vec![i as u8; expected_len];
assert_eq!(object, expected_bytes);
}
}
fn make_index(fmt: u8, off_size: u8, count: usize) -> BeBuffer {
let mut buf = BeBuffer::new();
match fmt {
1 => buf = buf.push(count as u16),
2 => buf = buf.push(count as u32),
_ => panic!("INDEX fmt should be 1 or 2"),
}
if count == 0 {
return buf;
}
buf = buf.push(off_size);
let mut offset = 1usize;
for i in 0..count + 1 {
buf = match off_size {
1 => buf.push(offset as u8),
2 => buf.push(offset as u16),
3 => buf.push(Uint24::checked_new(offset as u32).unwrap()),
4 => buf.push(offset as u32),
_ => panic!("off_size should be 1-4"),
};
offset += (i + 1) * 10;
}
for i in 0..count {
buf = buf.extend(std::iter::repeat(i as u8).take((i + 1) * 10));
}
buf
}
}