pub mod bitmap;
pub mod color;
pub mod outline;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum GlyphSource {
Sbix = 1,
Cbdt,
Ebdt,
Colr,
Varc,
Glyf,
Cff2,
Cff,
Type1,
}
impl GlyphSource {
const ALL: [Self; 9] = [
Self::Sbix,
Self::Cbdt,
Self::Ebdt,
Self::Colr,
Self::Varc,
Self::Glyf,
Self::Cff2,
Self::Cff,
Self::Type1,
];
const fn from_nibble(nibble: u8) -> Option<Self> {
let index = nibble as usize;
if index == 0 || index > Self::ALL.len() {
None
} else {
Some(Self::ALL[index - 1])
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct GlyphSourceOrder(u64);
impl GlyphSourceOrder {
pub const DEFAULT: Self = Self::new(&[
GlyphSource::Sbix,
GlyphSource::Cbdt,
GlyphSource::Colr,
GlyphSource::Varc,
GlyphSource::Glyf,
GlyphSource::Cff2,
GlyphSource::Cff,
GlyphSource::Type1,
]);
pub const ALL: Self = Self::new(&[
GlyphSource::Sbix,
GlyphSource::Cbdt,
GlyphSource::Ebdt,
GlyphSource::Colr,
GlyphSource::Varc,
GlyphSource::Glyf,
GlyphSource::Cff2,
GlyphSource::Cff,
GlyphSource::Type1,
]);
pub const OUTLINES: Self = Self::new(&[
GlyphSource::Varc,
GlyphSource::Glyf,
GlyphSource::Cff2,
GlyphSource::Cff,
GlyphSource::Type1,
]);
pub const BASE_OUTLINES: Self = Self::new(&[
GlyphSource::Glyf,
GlyphSource::Cff2,
GlyphSource::Cff,
GlyphSource::Type1,
]);
pub const BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt, GlyphSource::Ebdt]);
pub const COLOR_BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt]);
pub const MONO_BITMAPS: Self = Self::new(&[GlyphSource::Ebdt]);
pub const COLOR: Self = Self::new(&[GlyphSource::Colr]);
pub const EMPTY: Self = Self(0);
pub(crate) const MAX_LEN: usize = GlyphSource::ALL.len();
pub const fn new(sources: &[GlyphSource]) -> Self {
let mut packed = 0u64;
let mut len = 0;
let mut i = 0;
while i < sources.len() && len < Self::MAX_LEN {
let source = sources[i];
i += 1;
if Self(packed).contains(source) {
continue;
}
packed |= (source as u64) << (len * 4);
len += 1;
}
Self(packed)
}
pub const fn contains(self, source: GlyphSource) -> bool {
let mut packed = self.0;
while packed != 0 {
if (packed & 0xF) as u8 == source as u8 {
return true;
}
packed >>= 4;
}
false
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub fn iter(self) -> impl Iterator<Item = GlyphSource> {
let mut packed = self.0;
core::iter::from_fn(move || {
let source = GlyphSource::from_nibble((packed & 0xF) as u8)?;
packed >>= 4;
Some(source)
})
}
}
impl Default for GlyphSourceOrder {
fn default() -> Self {
Self::DEFAULT
}
}
impl core::fmt::Debug for GlyphSourceOrder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use bitmap::BitmapSource;
use outline::OutlineSource;
#[test]
fn narrow_sources_round_trip() {
for source in GlyphSource::ALL {
let outline = OutlineSource::try_from(source);
let bitmap = BitmapSource::try_from(source);
assert_eq!(outline.map(GlyphSource::from).unwrap_or(source), source);
assert_eq!(bitmap.map(GlyphSource::from).unwrap_or(source), source);
let kinds = usize::from(outline.is_ok()) + usize::from(bitmap.is_ok());
assert_eq!(
kinds,
usize::from(source != GlyphSource::Colr),
"{source:?}"
);
}
}
#[test]
fn named_orders_hold_their_sources() {
use GlyphSource::*;
let sources = |order: GlyphSourceOrder| order.iter().collect::<Vec<_>>();
assert_eq!(sources(GlyphSourceOrder::ALL), GlyphSource::ALL);
assert_eq!(
sources(GlyphSourceOrder::DEFAULT),
[Sbix, Cbdt, Colr, Varc, Glyf, Cff2, Cff, Type1]
);
assert_eq!(sources(GlyphSourceOrder::BITMAPS), [Sbix, Cbdt, Ebdt]);
assert_eq!(sources(GlyphSourceOrder::COLOR_BITMAPS), [Sbix, Cbdt]);
assert_eq!(sources(GlyphSourceOrder::MONO_BITMAPS), [Ebdt]);
assert_eq!(sources(GlyphSourceOrder::COLOR), [Colr]);
assert_eq!(
sources(GlyphSourceOrder::OUTLINES),
[Varc, Glyf, Cff2, Cff, Type1]
);
assert_eq!(
sources(GlyphSourceOrder::BASE_OUTLINES),
[Glyf, Cff2, Cff, Type1]
);
assert!(GlyphSourceOrder::EMPTY.is_empty());
assert_eq!(
alloc::format!("{:?}", GlyphSourceOrder::COLOR_BITMAPS),
"[Sbix, Cbdt]"
);
}
#[test]
fn order_keeps_first_position_of_each_source() {
let order = GlyphSourceOrder::new(&[
GlyphSource::Glyf,
GlyphSource::Varc,
GlyphSource::Glyf,
GlyphSource::Cff,
]);
let sources: Vec<_> = order.iter().collect();
assert_eq!(
sources,
[GlyphSource::Glyf, GlyphSource::Varc, GlyphSource::Cff]
);
assert!(order.contains(GlyphSource::Cff));
assert!(!order.contains(GlyphSource::Cff2));
}
#[test]
fn default_order_is_the_harfbuzz_extents_chain() {
let sources: Vec<_> = GlyphSourceOrder::DEFAULT.iter().collect();
assert_eq!(
sources,
[
GlyphSource::Sbix,
GlyphSource::Cbdt,
GlyphSource::Colr,
GlyphSource::Varc,
GlyphSource::Glyf,
GlyphSource::Cff2,
GlyphSource::Cff,
GlyphSource::Type1,
]
);
assert!(!GlyphSourceOrder::DEFAULT.contains(GlyphSource::Ebdt));
}
#[test]
fn empty_order_has_no_sources() {
assert!(GlyphSourceOrder::EMPTY.is_empty());
assert_eq!(GlyphSourceOrder::EMPTY.iter().count(), 0);
assert!(!GlyphSourceOrder::new(&[]).contains(GlyphSource::Glyf));
}
#[test]
fn every_source_round_trips_through_an_order() {
let order = GlyphSourceOrder::new(&GlyphSource::ALL);
let sources: Vec<_> = order.iter().collect();
assert_eq!(sources, GlyphSource::ALL);
}
}