#![deny(missing_docs)]
use bytemuck::AnyBitPattern;
use std::os::raw::{c_int, c_uint};
pub mod data;
pub mod ptr;
use data::{
CacheData, CharSetData, FontSetData, PatternData, PatternEltData, ValueData, ValueListData,
};
use ptr::{Array, Ptr};
type Result<T> = std::result::Result<T, Error>;
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub enum Value<'buf> {
Unknown,
Void,
Int(c_int),
Float(f64),
String(Ptr<'buf, u8>),
Bool(c_int),
Matrix(Ptr<'buf, ()>),
CharSet(CharSet<'buf>),
FtFace(Ptr<'buf, ()>),
LangSet(Ptr<'buf, ()>),
Range(Ptr<'buf, ()>),
}
impl<'buf> Ptr<'buf, ValueData> {
pub fn to_value(&self) -> Result<Value<'buf>> {
use crate::Value::*;
let payload = self.deref()?;
unsafe {
Ok(match payload.ty {
-1 => Unknown,
0 => Void,
1 => Int(payload.val.i),
2 => Float(payload.val.d),
3 => String(self.relative_offset(payload.val.s)?),
4 => Bool(payload.val.b),
5 => Matrix(self.relative_offset(payload.val.m)?),
6 => CharSet(crate::CharSet(self.relative_offset(payload.val.c)?)),
7 => FtFace(self.relative_offset(payload.val.f)?),
8 => LangSet(self.relative_offset(payload.val.l)?),
9 => Range(self.relative_offset(payload.val.r)?),
_ => return Err(Error::InvalidEnumTag(payload.ty)),
})
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum Object {
Invalid = 0,
Family,
FamilyLang,
Style,
StyleLang,
FullName,
FullNameLang,
Slant,
Weight,
Width,
Size,
Aspect,
PixelSize,
Spacing,
Foundry,
AntiAlias,
HintStyle,
Hinting,
VerticalLayout,
AutoHint,
GlobalAdvance,
File,
Index,
Rasterizer,
Outline,
Scalable,
Dpi,
Rgba,
Scale,
MinSpace,
CharWidth,
CharHeight,
Matrix,
CharSet,
Lang,
FontVersion,
Capability,
FontFormat,
Embolden,
EmbeddedBitmap,
Decorative,
LcdFilter,
NameLang,
FontFeatures,
PrgName,
Hash,
PostscriptName,
Color,
Symbol,
FontVariations,
Variable,
FontHasHint,
Order,
DesktopName,
NamedInstance,
FontWrapper,
}
const MAX_OBJECT: c_int = Object::FontWrapper as c_int;
impl TryFrom<c_int> for Object {
type Error = Error;
fn try_from(value: c_int) -> Result<Self> {
if value <= MAX_OBJECT {
Ok(unsafe { std::mem::transmute(value) })
} else {
Err(Error::InvalidObjectTag(value))
}
}
}
#[derive(Clone, Debug)]
struct ValueList<'buf>(pub Ptr<'buf, ValueListData>);
impl<'buf> ValueList<'buf> {
fn value(&self) -> Result<Value<'buf>> {
self.0
.relative_offset(ptr::offset(
std::mem::size_of_val(&self.0.deref()?.next) as isize
))
.and_then(|val_ptr| val_ptr.to_value())
}
}
#[derive(Clone, Debug)]
struct ValueListIter<'buf> {
next: Option<Result<ValueList<'buf>>>,
}
impl<'buf> Iterator for ValueListIter<'buf> {
type Item = Result<Value<'buf>>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.next.take();
if let Some(Ok(next)) = next {
match next.0.deref() {
Ok(next_payload) => {
if next_payload.next.0 == 0 {
self.next = None;
} else {
self.next = Some(next.0.relative_offset(next_payload.next).map(ValueList));
}
}
Err(e) => {
self.next = Some(Err(e));
}
}
Some(next.value())
} else if let Some(Err(e)) = next {
Some(Err(e))
} else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct Pattern<'buf>(pub Ptr<'buf, PatternData>);
impl Pattern<'_> {
pub fn elts(&self) -> Result<impl Iterator<Item = PatternElt> + '_> {
let payload = self.0.deref()?;
let elts = self.0.relative_offset(payload.elts_offset)?;
Ok(elts.array(payload.num)?.map(PatternElt))
}
pub fn data(&self) -> Result<PatternData> {
self.0.deref()
}
}
pub struct PatternElt<'buf>(pub Ptr<'buf, PatternEltData>);
impl<'buf> PatternElt<'buf> {
pub fn values(&self) -> Result<impl Iterator<Item = Result<Value<'buf>>> + 'buf> {
Ok(ValueListIter {
next: Some(Ok(ValueList(
self.0.relative_offset(self.0.deref()?.values)?,
))),
})
}
pub fn object(&self) -> Result<Object> {
self.0.deref()?.object.try_into()
}
pub fn data(&self) -> Result<PatternEltData> {
self.0.deref()
}
}
#[derive(Clone, Debug)]
pub struct FontSet<'buf>(pub Ptr<'buf, FontSetData>);
impl<'buf> FontSet<'buf> {
pub fn fonts<'a>(&'a self) -> Result<impl Iterator<Item = Result<Pattern<'buf>>> + 'a> {
let payload = self.0.deref()?;
let fonts = self
.0
.relative_offset(payload.fonts)?
.array(payload.nfont)?;
let me = self.clone();
Ok(fonts.map(move |font_offset| Ok(Pattern(me.0.relative_offset(font_offset.deref()?)?))))
}
pub fn data(&self) -> Result<FontSetData> {
self.0.deref()
}
}
#[derive(Clone, Debug)]
pub struct CharSet<'buf>(pub Ptr<'buf, CharSetData>);
impl<'buf> CharSet<'buf> {
pub fn leaves(&self) -> Result<impl Iterator<Item = Result<CharSetLeaf>> + 'buf> {
let payload = self.0.deref()?;
let leaf_array = self.0.relative_offset(payload.leaves)?;
Ok(leaf_array.array(payload.num)?.map(move |leaf_offset| {
leaf_array
.relative_offset(leaf_offset.deref()?)
.and_then(|leaf_ptr| leaf_ptr.deref())
}))
}
pub fn numbers(&self) -> Result<Array<'buf, u16>> {
let payload = self.0.deref()?;
self.0.relative_offset(payload.numbers)?.array(payload.num)
}
pub fn chars(&self) -> Result<impl Iterator<Item = Result<u32>> + 'buf> {
fn transpose_result_iter<T: 'static, I: Iterator<Item = T> + 'static>(
res: Result<I>,
) -> impl Iterator<Item = Result<T>> {
match res {
Ok(iter) => Box::new(iter.map(|x| Ok(x))) as Box<dyn Iterator<Item = Result<T>>>,
Err(e) => Box::new(Some(Err(e)).into_iter()) as Box<dyn Iterator<Item = Result<T>>>,
}
}
let leaves = self.leaves()?;
let numbers = self.numbers()?;
Ok(leaves.zip(numbers).flat_map(|(leaf, number)| {
let iter = (move || {
let number = (number.deref()? as u32) << 8;
Ok(leaf?.iter().map(move |x| x as u32 + number))
})();
transpose_result_iter(iter)
}))
}
pub fn leaf_at(&self, idx: usize) -> Result<Option<CharSetLeaf>> {
let payload = self.0.deref()?;
let leaf_array = self.0.relative_offset(payload.leaves)?;
leaf_array
.array(payload.num)?
.get(idx)
.map(|ptr| {
leaf_array
.relative_offset(ptr.deref()?)
.and_then(|leaf_ptr| leaf_ptr.deref())
})
.transpose()
}
pub fn contains(&self, ch: u32) -> Result<bool> {
let hi = ((ch >> 8) & 0xffff) as u16;
let lo = (ch & 0xff) as u8;
match self.numbers()?.as_slice()?.binary_search(&hi) {
Ok(idx) => Ok(self.leaf_at(idx)?.unwrap().contains_byte(lo)),
Err(_) => Ok(false),
}
}
}
#[derive(AnyBitPattern, Copy, Clone, Debug)]
#[repr(C)]
pub struct CharSetLeaf {
pub map: [u32; 8],
}
impl CharSetLeaf {
pub fn contains_byte(&self, byte: u8) -> bool {
let map_idx = (byte >> 5) as usize;
let bit_idx = (byte & 0x1f) as u32;
(self.map[map_idx] >> bit_idx) & 1 != 0
}
pub fn iter(self) -> CharSetLeafIter {
CharSetLeafIter {
leaf: self,
map_idx: 0,
}
}
}
impl IntoIterator for CharSetLeaf {
type Item = u8;
type IntoIter = CharSetLeafIter;
fn into_iter(self) -> CharSetLeafIter {
self.iter()
}
}
#[derive(Clone, Debug)]
pub struct CharSetLeafIter {
leaf: CharSetLeaf,
map_idx: u8,
}
impl Iterator for CharSetLeafIter {
type Item = u8;
fn next(&mut self) -> Option<u8> {
let len = self.leaf.map.len() as u8;
if self.map_idx >= len {
None
} else {
let bits = &mut self.leaf.map[self.map_idx as usize];
if *bits != 0 {
let ret = bits.trailing_zeros() as u8;
*bits &= !(1 << ret);
Some(ret + (self.map_idx << 5))
} else {
while self.map_idx < len && self.leaf.map[self.map_idx as usize] == 0 {
self.map_idx += 1;
}
self.next()
}
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("Invalid magic number {0:#x}")]
BadMagic(c_uint),
#[error("Unsupported version {0}")]
UnsupportedVersion(c_int),
#[error("Bad pointer {0}")]
BadPointer(isize),
#[error("Bad offset {0}")]
BadOffset(isize),
#[error("Bad alignment (expected {expected_alignment}) for offset {offset}")]
BadAlignment {
expected_alignment: usize,
offset: usize,
},
#[error("Bad length {0}")]
BadLength(isize),
#[error("Invalid enum tag {0}")]
InvalidEnumTag(c_int),
#[error("Invalid object tag {0}")]
InvalidObjectTag(c_int),
#[error("Unterminated string at {0}")]
UnterminatedString(isize),
#[error("Wrong size: header expects {expected} bytes, buffer is {actual} bytes")]
WrongSize { expected: isize, actual: isize },
}
#[derive(Clone, Debug)]
pub struct Cache<'buf>(Ptr<'buf, CacheData>);
impl<'buf> Cache<'buf> {
pub fn from_bytes(buf: &'buf [u8]) -> Result<Self> {
use Error::*;
let len = std::mem::size_of::<CacheData>();
if buf.len() < len {
Err(WrongSize {
expected: len as isize,
actual: buf.len() as isize,
})
} else {
let cache: CacheData = bytemuck::try_pod_read_unaligned(&buf[0..len])
.expect("but we checked the length...");
if cache.magic != 4228054020 {
Err(BadMagic(cache.magic))
} else if cache.version < 7 || cache.version > 9 {
Err(UnsupportedVersion(cache.version))
} else if cache.size != buf.len() as isize {
Err(WrongSize {
expected: cache.size,
actual: buf.len() as isize,
})
} else {
Ok(Cache(Ptr {
buf,
offset: 0,
marker: std::marker::PhantomData,
}))
}
}
}
pub fn set(&self) -> Result<FontSet<'buf>> {
Ok(FontSet(self.0.relative_offset(self.0.deref()?.set)?))
}
pub fn data(&self) -> Result<CacheData> {
self.0.deref()
}
}