mod language;
pub mod name;
pub mod os2;
mod parser;
pub use language::Language;
pub use name::{name_id, PlatformId};
pub use os2::{Style, Width};
pub use parser::LazyArray16;
use parser::{FromData, NumFrom, Offset, Offset32, Stream};
#[derive(Clone, Copy, PartialEq, Debug)]
enum Magic {
TrueType,
OpenType,
FontCollection,
}
impl FromData for Magic {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
match u32::parse(data)? {
0x00010000 | 0x74727565 => Some(Magic::TrueType),
0x4F54544F => Some(Magic::OpenType),
0x74746366 => Some(Magic::FontCollection),
_ => None,
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Tag(pub u32);
impl Tag {
#[inline]
pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
Tag(((bytes[0] as u32) << 24)
| ((bytes[1] as u32) << 16)
| ((bytes[2] as u32) << 8)
| (bytes[3] as u32))
}
}
impl FromData for Tag {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
u32::parse(data).map(Tag)
}
}
#[derive(Clone, Copy)]
struct TableRecord {
tag: Tag,
offset: u32,
length: u32,
}
impl FromData for TableRecord {
const SIZE: usize = 16;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
let tag = s.read::<Tag>()?;
s.skip::<u32>(); Some(TableRecord {
tag,
offset: s.read::<u32>()?,
length: s.read::<u32>()?,
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FaceParsingError {
MalformedFont,
UnknownMagic,
FaceIndexOutOfBounds,
}
#[derive(Clone, Copy)]
pub struct RawFace<'a> {
pub data: &'a [u8],
table_records: LazyArray16<'a, TableRecord>,
}
impl<'a> RawFace<'a> {
pub fn parse(data: &'a [u8], index: u32) -> Result<Self, FaceParsingError> {
let mut s = Stream::new(data);
let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
if magic == Magic::FontCollection {
s.skip::<u32>(); let number_of_faces = s.read::<u32>().ok_or(FaceParsingError::MalformedFont)?;
let offsets = s
.read_array32::<Offset32>(number_of_faces)
.ok_or(FaceParsingError::MalformedFont)?;
let face_offset = offsets
.get(index)
.ok_or(FaceParsingError::FaceIndexOutOfBounds)?;
let face_offset = face_offset
.to_usize()
.checked_sub(s.offset())
.ok_or(FaceParsingError::MalformedFont)?;
s.advance_checked(face_offset)
.ok_or(FaceParsingError::MalformedFont)?;
let magic = s.read::<Magic>().ok_or(FaceParsingError::UnknownMagic)?;
if magic == Magic::FontCollection {
return Err(FaceParsingError::UnknownMagic);
}
} else {
if index != 0 {
return Err(FaceParsingError::FaceIndexOutOfBounds);
}
}
let num_tables = s.read::<u16>().ok_or(FaceParsingError::MalformedFont)?;
s.advance(6); let table_records = s
.read_array16::<TableRecord>(num_tables)
.ok_or(FaceParsingError::MalformedFont)?;
Ok(RawFace {
data,
table_records,
})
}
pub fn table(&self, tag: Tag) -> Option<&'a [u8]> {
let (_, table) = self
.table_records
.binary_search_by(|record| record.tag.cmp(&tag))?;
let offset = usize::num_from(table.offset);
let length = usize::num_from(table.length);
let end = offset.checked_add(length)?;
self.data.get(offset..end)
}
}
impl core::fmt::Debug for RawFace<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "RawFace {{ ... }}")
}
}
#[inline]
pub fn fonts_in_collection(data: &[u8]) -> Option<u32> {
let mut s = Stream::new(data);
if s.read::<Magic>()? != Magic::FontCollection {
return None;
}
s.skip::<u32>(); s.read::<u32>()
}