use crate::display::{Quad, QuadKind};
use crate::geom::Vec2;
use crate::input::{EditKey, InputEvent, KeyCode, KeyMods, KeyPress, Mods, UiEvent};
use crate::runtime::Core;
pub fn click(core: &mut Core, at: Vec2) -> Vec<UiEvent> {
let mut out = core.handle_input(InputEvent::CursorMoved(at));
out.extend(core.handle_input(InputEvent::mouse_down(1)));
out.extend(core.handle_input(InputEvent::mouse_up()));
out
}
pub fn click_at(core: &mut Core, x: f32, y: f32) -> Vec<UiEvent> {
click(core, Vec2::new(x, y))
}
pub fn press(core: &mut Core, at: Vec2) -> Vec<UiEvent> {
let mut out = core.handle_input(InputEvent::CursorMoved(at));
out.extend(core.handle_input(InputEvent::mouse_down(1)));
out
}
pub fn release(core: &mut Core) -> Vec<UiEvent> {
core.handle_input(InputEvent::mouse_up())
}
pub fn hover(core: &mut Core, at: Vec2) -> Vec<UiEvent> {
core.handle_input(InputEvent::CursorMoved(at))
}
pub fn tab(core: &mut Core, shift: bool) -> Vec<UiEvent> {
edit_key(
core,
EditKey::Tab,
Mods {
shift,
..Mods::default()
},
)
}
pub fn edit_key(core: &mut Core, key: EditKey, mods: Mods) -> Vec<UiEvent> {
core.handle_input(InputEvent::Key(key, mods))
}
pub fn drive(core: &mut Core, events: &[InputEvent]) -> Vec<UiEvent> {
let mut out = Vec::new();
for ev in events {
out.extend(core.handle_input(ev.clone()));
}
out
}
pub fn kinds(evs: &[UiEvent]) -> Vec<&str> {
evs.iter()
.map(|e| match e.kind() {
Some(k) => k,
None => e.payload.as_str().unwrap_or("?"),
})
.collect()
}
pub fn codes(ws: &[crate::diag::Warning]) -> Vec<&'static str> {
ws.iter().map(|w| w.code).collect()
}
pub fn quads_of(core: &mut Core, kind: QuadKind) -> Vec<Quad> {
let (dl, _) = core.output();
dl.quads
.iter()
.filter(|q| q.kind == kind)
.cloned()
.collect()
}
pub fn solids(core: &mut Core) -> Vec<Quad> {
quads_of(core, QuadKind::Solid)
}
pub fn key_press(code: KeyCode) -> InputEvent {
InputEvent::KeyDown(KeyPress::new(code, KeyMods::default()))
}
pub fn key_release(code: KeyCode) -> InputEvent {
InputEvent::KeyUp(KeyPress::new(code, KeyMods::default()))
}
pub fn key_down(core: &mut Core, code: KeyCode, mods: KeyMods) -> Vec<UiEvent> {
core.handle_input(InputEvent::KeyDown(KeyPress::new(code, mods)))
}
pub fn key_up(core: &mut Core, code: KeyCode, mods: KeyMods) -> Vec<UiEvent> {
core.handle_input(InputEvent::KeyUp(KeyPress::new(code, mods)))
}
pub fn tags(evs: &[UiEvent]) -> Vec<&str> {
evs.iter()
.filter_map(|e| {
e.payload
.as_str()
.or_else(|| e.payload.get("tag")?.as_str())
})
.collect()
}
pub fn liga_font() -> Vec<u8> {
liga_font::build(&liga_font::Face::LIGA)
}
pub fn font_face(family: &str, weight: u16, italic: bool, fixed_pitch: bool) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
weight,
italic,
fixed_pitch,
..liga_font::Face::LIGA
})
}
pub fn han_face(family: &str) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
han: true,
..liga_font::Face::LIGA
})
}
pub fn mono_han_face(family: &str) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
fixed_pitch: true,
han: true,
wide_han: true,
..liga_font::Face::LIGA
})
}
pub fn han_only_face(family: &str) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
han: true,
ascii: false,
..liga_font::Face::LIGA
})
}
pub fn unmeasurable_face(family: &str) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
fixed_pitch: true,
han: true,
metrics: false,
..liga_font::Face::LIGA
})
}
pub fn variable_face(
family: &str,
italic: bool,
wght: [u16; 3],
instances: &[(&str, u16)],
) -> Vec<u8> {
liga_font::build(&liga_font::Face {
family,
italic,
fixed_pitch: true,
wght: Some(liga_font::Wght {
axis: wght,
instances,
}),
..liga_font::Face::LIGA
})
}
mod liga_font {
pub(super) struct Face<'a> {
pub(super) family: &'a str,
pub(super) weight: u16,
pub(super) italic: bool,
pub(super) fixed_pitch: bool,
pub(super) han: bool,
pub(super) wide_han: bool,
pub(super) ascii: bool,
pub(super) metrics: bool,
pub(super) wght: Option<Wght<'a>>,
}
pub(super) struct Wght<'a> {
pub(super) axis: [u16; 3],
pub(super) instances: &'a [(&'a str, u16)],
}
impl Face<'static> {
pub(super) const LIGA: Self = Self {
family: "Kui Liga",
weight: 400,
italic: false,
fixed_pitch: false,
han: false,
wide_han: false,
ascii: true,
metrics: true,
wght: None,
};
}
impl Face<'_> {
fn subfamily(&self) -> &'static str {
match (self.weight >= 600, self.italic) {
(false, false) => "Regular",
(true, false) => "Bold",
(false, true) => "Italic",
(true, true) => "Bold Italic",
}
}
}
struct W(Vec<u8>);
impl W {
fn u8(&mut self, v: u8) {
self.0.push(v);
}
fn u16(&mut self, v: u16) {
self.0.extend_from_slice(&v.to_be_bytes());
}
fn i16(&mut self, v: i16) {
self.0.extend_from_slice(&v.to_be_bytes());
}
fn u32(&mut self, v: u32) {
self.0.extend_from_slice(&v.to_be_bytes());
}
fn i64(&mut self, v: i64) {
self.0.extend_from_slice(&v.to_be_bytes());
}
fn bytes(&mut self, b: &[u8]) {
self.0.extend_from_slice(b);
}
fn pad4(&mut self) {
while !self.0.len().is_multiple_of(4) {
self.0.push(0);
}
}
}
const BOX: u16 = 1;
const F: u16 = 2;
const I: u16 = 3;
const FI: u16 = 4;
const GLYPHS: u16 = 5;
const FIRST: u16 = 0x20;
const LAST: u16 = 0x7E;
const HAN: u16 = 0x5B57;
fn square(right: i16) -> Vec<u8> {
let mut w = W(Vec::new());
w.i16(1); w.i16(50);
w.i16(0);
w.i16(right);
w.i16(700);
w.u16(3); w.u16(0); for _ in 0..4 {
w.u8(0x01); }
for dx in [50, 0, right - 50, 0] {
w.i16(dx);
}
for dy in [0, 700, 0, -700] {
w.i16(dy);
}
w.pad4();
w.0
}
fn glyf_and_loca() -> (Vec<u8>, Vec<u8>) {
let glyphs: [Vec<u8>; GLYPHS as usize] = [
Vec::new(),
square(450),
square(450),
square(450),
square(650),
];
let mut glyf = W(Vec::new());
let mut loca = W(Vec::new());
for g in &glyphs {
loca.u32(glyf.0.len() as u32);
glyf.bytes(g);
}
loca.u32(glyf.0.len() as u32);
(glyf.0, loca.0)
}
fn head(face: &Face) -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0001_0000); w.u32(0x0001_0000); w.u32(0); w.u32(0x5F0F_3CF5); w.u16(0x000B); w.u16(1000); w.i64(0); w.i64(0); w.i16(0); w.i16(0); w.i16(700); w.i16(700); w.u16(u16::from(face.weight >= 600) | u16::from(face.italic) << 1);
w.u16(8); w.i16(2); w.i16(1); w.i16(0); w.0
}
fn hhea() -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0001_0000);
w.i16(800); w.i16(-200); w.i16(0); w.u16(700); w.i16(0); w.i16(0); w.i16(700); w.i16(1); w.i16(0); w.i16(0); for _ in 0..4 {
w.i16(0);
}
w.i16(0); w.u16(GLYPHS); w.0
}
fn maxp() -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0001_0000);
w.u16(GLYPHS);
w.u16(4); w.u16(1); w.u16(0); w.u16(0); w.u16(2); for _ in 0..8 {
w.u16(0);
}
w.0
}
fn os2(face: &Face) -> Vec<u8> {
let mut w = W(Vec::new());
w.u16(4); w.i16(500); w.u16(face.weight); w.u16(5); w.u16(0); for v in [650, 600, 0, 75, 650, 600, 0, 350, 50, 350] {
w.i16(v); }
w.i16(0); w.bytes(&[0; 10]); w.u32(u32::from(face.ascii)); w.u32(u32::from(face.han) << 27);
w.u32(0);
w.u32(0);
w.bytes(b"KUI "); w.u16(match (face.weight >= 600, face.italic) {
(false, false) => 0x0040,
(bold, italic) => u16::from(italic) | u16::from(bold) << 5,
});
w.u16(if face.ascii { FIRST } else { HAN }); w.u16(if face.han { HAN } else { LAST }); w.i16(800); w.i16(-200); w.i16(0); w.u16(800); w.u16(200); w.u32(1); w.u32(0);
w.i16(500); w.i16(700); w.u16(0); w.u16(0x20); w.u16(2); debug_assert_eq!(w.0.len(), 96);
w.0
}
fn hmtx() -> Vec<u8> {
let mut w = W(Vec::new());
for g in 0..GLYPHS {
w.u16(if g == FI { 700 } else { 500 });
w.i16(0);
}
w.0
}
fn cmap(face: &Face) -> Vec<u8> {
if !face.ascii {
return cmap_han_alone(face);
}
let chars = usize::from(LAST - FIRST + 1);
let han: &[u16] = if face.han { &[HAN] } else { &[] };
let segments = 2 + han.len() as u16;
let entry_selector = segments.ilog2() as u16;
let search_range = 2 << entry_selector;
let mut w = W(Vec::new());
w.u16(0); w.u16(1); w.u16(3); w.u16(1); w.u32(12); let length = 14 + 8 * usize::from(segments) + 2 + 2 * chars;
w.u16(4); w.u16(length as u16);
w.u16(0); w.u16(segments * 2);
w.u16(search_range);
w.u16(entry_selector);
w.u16(segments * 2 - search_range); w.u16(LAST); han.iter().for_each(|&c| w.u16(c));
w.u16(0xFFFF);
w.u16(0); w.u16(FIRST); han.iter().for_each(|&c| w.u16(c));
w.u16(0xFFFF);
w.i16(0); han.iter()
.for_each(|&c| w.u16(han_glyph(face).wrapping_sub(c)));
w.i16(1);
w.u16(2 * segments); han.iter().for_each(|_| w.u16(0));
w.u16(0);
for c in FIRST..=LAST {
w.u16(match c {
0x66 => F,
0x69 => I,
_ => BOX,
});
}
debug_assert_eq!(w.0.len(), 12 + length);
w.0
}
fn han_glyph(face: &Face) -> u16 {
if face.wide_han { FI } else { BOX }
}
fn cmap_han_alone(face: &Face) -> Vec<u8> {
let segments: u16 = 2;
let entry_selector = segments.ilog2() as u16;
let search_range = 2 << entry_selector;
let length = 14 + 8 * usize::from(segments) + 2;
let mut w = W(Vec::new());
w.u16(0); w.u16(1); w.u16(3); w.u16(1); w.u32(12); w.u16(4); w.u16(length as u16);
w.u16(0); w.u16(segments * 2);
w.u16(search_range);
w.u16(entry_selector);
w.u16(segments * 2 - search_range); w.u16(HAN); w.u16(0xFFFF);
w.u16(0); w.u16(HAN); w.u16(0xFFFF);
w.u16(han_glyph(face).wrapping_sub(HAN)); w.i16(1);
w.u16(0); w.u16(0);
debug_assert_eq!(w.0.len(), 12 + length);
w.0
}
fn name(face: &Face) -> Vec<u8> {
let full = match face.subfamily() {
"Regular" => face.family.to_string(),
sub => format!("{} {sub}", face.family),
};
let postscript: String = full.chars().filter(|c| !c.is_whitespace()).collect();
let mut strings: Vec<(u16, &str)> = vec![
(1, face.family),
(2, face.subfamily()),
(4, &full),
(6, &postscript),
];
if let Some(wght) = &face.wght {
strings.push((AXIS_NAME, "Weight"));
for (i, (name, _)) in wght.instances.iter().enumerate() {
strings.push((AXIS_NAME + 1 + i as u16, name));
}
}
let mut w = W(Vec::new());
w.u16(0); w.u16(strings.len() as u16);
w.u16(6 + 12 * strings.len() as u16); let mut pool = W(Vec::new());
for (id, s) in strings.iter().copied() {
let start = pool.0.len() as u16;
for unit in s.encode_utf16() {
pool.u16(unit);
}
w.u16(3); w.u16(1); w.u16(0x0409); w.u16(id);
w.u16((s.encode_utf16().count() * 2) as u16);
w.u16(start);
}
w.bytes(&pool.0);
w.0
}
fn post(face: &Face) -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0003_0000); w.u32(if face.italic {
(-12i32 << 16) as u32
} else {
0
});
w.i16(-100); w.i16(50); w.u32(u32::from(face.fixed_pitch)); for _ in 0..4 {
w.u32(0);
}
w.0
}
fn gsub() -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0001_0000);
w.u16(10); w.u16(36); w.u16(50); w.u16(2);
w.bytes(b"DFLT");
w.u16(14);
w.bytes(b"latn");
w.u16(14);
w.u16(4); w.u16(0); w.u16(0); w.u16(0xFFFF); w.u16(1); w.u16(0); w.u16(1);
w.bytes(b"liga");
w.u16(8);
w.u16(0); w.u16(1); w.u16(0); w.u16(1);
w.u16(4);
w.u16(4); w.u16(0); w.u16(1); w.u16(8); w.u16(1); w.u16(8); w.u16(1); w.u16(14); w.u16(1); w.u16(1); w.u16(F);
w.u16(1); w.u16(4); w.u16(FI); w.u16(2); w.u16(I); debug_assert_eq!(w.0.len(), 86);
w.0
}
const AXIS_NAME: u16 = 256;
fn fixed(v: u16) -> u32 {
u32::from(v) << 16
}
fn fvar(wght: &Wght) -> Vec<u8> {
let mut w = W(Vec::new());
w.u32(0x0001_0000);
w.u16(16); w.u16(2); w.u16(1); w.u16(20); w.u16(wght.instances.len() as u16);
w.u16(8); w.bytes(b"wght");
for v in wght.axis {
w.u32(fixed(v)); }
w.u16(0); w.u16(AXIS_NAME);
for (i, &(_, at)) in wght.instances.iter().enumerate() {
w.u16(AXIS_NAME + 1 + i as u16);
w.u16(0);
w.u32(fixed(at));
}
w.0
}
fn gvar() -> Vec<u8> {
let mut data = W(Vec::new());
data.u16(1); data.u16(10); data.u16(7); data.u16(0xA000); data.u16(0x4000); data.u8(0); data.bytes(&[0x81, 0x01, 100, 100, 0x83]); data.u8(0x87); data.pad4();
let per_glyph = data.0;
let mut w = W(Vec::new());
w.u16(1);
w.u16(0);
w.u16(1); w.u16(0); let array = 20 + 4 * (u32::from(GLYPHS) + 1);
w.u32(array); w.u16(GLYPHS);
w.u16(1); w.u32(array); w.u32(0);
for g in 1..=GLYPHS {
w.u32((per_glyph.len() * usize::from(g - 1)) as u32);
}
for _ in 1..GLYPHS {
w.bytes(&per_glyph);
}
w.0
}
const METRICS: [&[u8; 4]; 5] = [b"glyf", b"head", b"hhea", b"hmtx", b"loca"];
fn checksum(table: &[u8]) -> u32 {
table
.chunks(4)
.map(|c| {
let mut word = [0u8; 4];
word[..c.len()].copy_from_slice(c);
u32::from_be_bytes(word)
})
.fold(0u32, u32::wrapping_add)
}
pub(super) fn build(face: &Face) -> Vec<u8> {
let (glyf, loca) = glyf_and_loca();
let mut tables: Vec<(&[u8; 4], Vec<u8>)> = [
(b"GSUB", gsub()),
(b"OS/2", os2(face)),
(b"cmap", cmap(face)),
(b"glyf", glyf),
(b"head", head(face)),
(b"hhea", hhea()),
(b"hmtx", hmtx()),
(b"loca", loca),
(b"maxp", maxp()),
(b"name", name(face)),
(b"post", post(face)),
]
.into_iter()
.filter(|(tag, _)| face.metrics || !METRICS.contains(tag))
.collect();
if let Some(wght) = &face.wght {
tables.push((b"fvar", fvar(wght)));
tables.push((b"gvar", gvar()));
tables.sort_by_key(|(tag, _)| **tag);
}
let n = tables.len() as u16;
let mut w = W(Vec::new());
w.u32(0x0001_0000);
w.u16(n);
let mut search_range = 16;
let mut entry_selector = 0;
while search_range * 2 <= 16 * n {
search_range *= 2;
entry_selector += 1;
}
w.u16(search_range);
w.u16(entry_selector);
w.u16(16 * n - search_range);
let mut offset = 12 + 16 * usize::from(n);
for (tag, table) in &tables {
w.bytes(*tag);
w.u32(checksum(table));
w.u32(offset as u32);
w.u32(table.len() as u32);
offset += table.len().div_ceil(4) * 4;
}
for (_, table) in &tables {
w.bytes(table);
w.pad4();
}
w.0
}
}