use crate::figfont::FIGfont;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutMode {
FullWidth,
Kerning,
UniversalSmush,
RuleSmush(u8),
OverlapOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Justify {
Center,
Left,
Right,
FontDefault,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutFlag {
Kerning,
FullWidth,
ForceSmush,
FontDefaultSmush,
OverlapOnly,
Explicit(i32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JustifyFlag {
Center,
Left,
Right,
FontDefault,
}
#[derive(Debug, Clone, Default)]
pub struct LayoutFlags {
pub flags: Vec<LayoutFlag>,
}
#[derive(Debug, Clone, Default)]
pub struct JustifyFlags {
pub flags: Vec<JustifyFlag>,
}
pub struct LayoutResolver;
impl LayoutResolver {
pub fn resolve(font: &FIGfont, flags: &LayoutFlags) -> LayoutMode {
if let Some(last) = flags.flags.last() {
return match *last {
LayoutFlag::Kerning => LayoutMode::Kerning,
LayoutFlag::FullWidth => LayoutMode::FullWidth,
LayoutFlag::ForceSmush => {
let bits = (font.full_layout & 0b0011_1111) as u8;
if bits == 0 {
LayoutMode::UniversalSmush
} else {
LayoutMode::RuleSmush(bits)
}
}
LayoutFlag::FontDefaultSmush => font_default_mode(font),
LayoutFlag::OverlapOnly => LayoutMode::OverlapOnly,
LayoutFlag::Explicit(n) => explicit_mode(n),
};
}
font_default_mode(font)
}
}
fn font_default_mode(font: &FIGfont) -> LayoutMode {
let smushing = font.full_layout & (crate::smush::RULE_HORIZONTAL_SMUSHING as u32) != 0;
let kerning = font.full_layout & (crate::smush::RULE_HORIZONTAL_KERNING as u32) != 0;
let bits = (font.full_layout & 0b0011_1111) as u8;
if smushing {
if bits == 0 {
LayoutMode::UniversalSmush
} else {
LayoutMode::RuleSmush(bits)
}
} else if kerning {
LayoutMode::Kerning
} else {
LayoutMode::FullWidth
}
}
fn explicit_mode(n: i32) -> LayoutMode {
match n {
-1 => LayoutMode::FullWidth,
0 => LayoutMode::Kerning,
-2 => LayoutMode::Kerning,
bits if (1..=63).contains(&bits) => LayoutMode::RuleSmush(bits as u8),
_ => LayoutMode::FullWidth,
}
}
pub fn resolve_justify(flags: &JustifyFlags) -> Justify {
flags
.flags
.last()
.map(|f| match *f {
JustifyFlag::Center => Justify::Center,
JustifyFlag::Left => Justify::Left,
JustifyFlag::Right => Justify::Right,
JustifyFlag::FontDefault => Justify::FontDefault,
})
.unwrap_or(Justify::FontDefault)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::figfont::parse_bytes;
fn font() -> FIGfont {
parse_bytes(crate::figfont::BUNDLED_FONTS[0].1).expect("bundled font parses")
}
#[test]
fn empty_flags_yields_font_default() {
let mode = LayoutResolver::resolve(&font(), &LayoutFlags::default());
let _ = mode; }
#[test]
fn last_wins_layout_kerning() {
let f = LayoutFlags {
flags: vec![
LayoutFlag::FullWidth,
LayoutFlag::ForceSmush,
LayoutFlag::Kerning,
],
};
assert_eq!(LayoutResolver::resolve(&font(), &f), LayoutMode::Kerning);
}
#[test]
fn explicit_layout_bitfield_24() {
let f = LayoutFlags {
flags: vec![LayoutFlag::Explicit(24)],
};
assert_eq!(
LayoutResolver::resolve(&font(), &f),
LayoutMode::RuleSmush(24)
);
}
#[test]
fn explicit_layout_zero_is_kerning() {
let f = LayoutFlags {
flags: vec![LayoutFlag::Explicit(0)],
};
assert_eq!(LayoutResolver::resolve(&font(), &f), LayoutMode::Kerning);
}
#[test]
fn justify_last_wins() {
let j = JustifyFlags {
flags: vec![JustifyFlag::Left, JustifyFlag::Center, JustifyFlag::Right],
};
assert_eq!(resolve_justify(&j), Justify::Right);
}
#[test]
fn justify_empty_yields_font_default() {
assert_eq!(
resolve_justify(&JustifyFlags::default()),
Justify::FontDefault
);
}
}