glyph.rs
4.2 kB · rust · 143 lines
1use super::bitmaps::{DIGITS, EXTRAS, SPECIALS, UPPERS};2use serde::{Deserialize, Serialize};34/// One character's pixel bitmap.5#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]6pub struct Glyph {7 /// The character the glyph draws.8 pub char: char,9 /// The bitmap rows of '0' and '1' characters.10 pub rows: Vec<String>,11}1213impl Glyph {14 /// Builds a glyph from its character and rows.15 pub fn new(char: char, rows: Vec<String>) -> Glyph {16 Glyph { char, rows }17 }18 /// Returns the cell width of the first row, or 0 for an empty glyph.19 pub fn width(&self) -> usize {20 self.rows.first().map_or(0, |row| row.chars().count())21 }22 /// Returns the number of rows.23 pub fn height(&self) -> usize {24 self.rows.len()25 }26}2728/// The characters that dip below the five-row baseline.29pub(crate) const DESCENDERS: &[char] = &[30 '@', '$', '\u{00a9}', '\u{00ae}', '(', ')', '[', ']', '{', '}',31];3233/// Returns whether the character dips below the baseline.34pub(crate) fn descends(c: char) -> bool {35 DESCENDERS.contains(&c)36}3738/// Cuts blank edge columns from a bitmap, collapsing an all-blank one to a single '0' column; a row shorter than the cut keeps what it has.39pub fn trim(rows: &[String]) -> Vec<String> {40 let grid: Vec<Vec<char>> = rows.iter().map(|row| row.chars().collect()).collect();41 let width = grid.iter().map(Vec::len).max().unwrap_or(0);42 if width == 0 {43 return rows.to_vec();44 }45 let lit = |col: usize| grid.iter().any(|row| row.get(col) == Some(&'1'));46 let (Some(start), Some(end)) = (47 (0..width).find(|&col| lit(col)),48 (0..width).rev().find(|&col| lit(col)),49 ) else {50 return grid.iter().map(|_| "0".to_string()).collect();51 };52 grid.iter()53 .map(|row| row.iter().skip(start).take(end + 1 - start).collect())54 .collect()55}5657/// Blanks the four corner cells of an uppercase bitmap into its rounded lowercase form.58pub fn lower(rows: &[&str]) -> Vec<String> {59 let mut grid: Vec<Vec<char>> = rows.iter().map(|row| row.chars().collect()).collect();60 let last = grid.len().saturating_sub(1);61 for y in [0, last] {62 if let Some(row) = grid.get_mut(y) {63 if let Some(first) = row.first_mut() {64 *first = '0';65 }66 if let Some(end) = row.last_mut() {67 *end = '0';68 }69 }70 }71 grid.into_iter()72 .map(|row| row.into_iter().collect())73 .collect()74}7576fn rows_of(rows: &[&str]) -> Vec<String> {77 rows.iter().map(|row| row.to_string()).collect()78}7980/// Builds the twenty-six uppercase glyphs.81pub fn uppers() -> Vec<Glyph> {82 UPPERS83 .iter()84 .map(|&(c, rows)| Glyph::new(c, rows_of(rows)))85 .collect()86}8788/// Builds the twenty-six lowercase glyphs by rounding the uppers' corners.89pub fn lowers() -> Vec<Glyph> {90 UPPERS91 .iter()92 .map(|&(c, rows)| {93 let lowered = c.to_ascii_lowercase();94 Glyph::new(lowered, lower(rows))95 })96 .collect()97}9899/// Builds the ten digit glyphs.100pub fn digits() -> Vec<Glyph> {101 DIGITS102 .iter()103 .map(|&(c, rows)| Glyph::new(c, rows_of(rows)))104 .collect()105}106107/// Builds the punctuation, symbol and arrow glyphs.108pub fn extras() -> Vec<Glyph> {109 EXTRAS110 .iter()111 .map(|&(c, rows)| Glyph::new(c, rows_of(rows)))112 .collect()113}114115/// Builds the four seven-row glyphs: dollar, at, copyright and registered.116pub fn specials() -> Vec<Glyph> {117 SPECIALS118 .iter()119 .map(|&(c, rows)| Glyph::new(c, rows_of(rows)))120 .collect()121}122123/// Builds every glyph in font order: uppers, lowers, digits, extras, specials.124pub fn all() -> Vec<Glyph> {125 let mut glyphs = uppers();126 glyphs.extend(lowers());127 glyphs.extend(digits());128 glyphs.extend(extras());129 glyphs.extend(specials());130 glyphs131}132133#[cfg(test)]134mod tests {135 use super::*;136137 #[test]138 fn trim_cuts_blank_edge_columns_and_collapses_an_empty_bitmap() {139 let rows = vec!["00100".to_string(), "00100".to_string()];140 assert_eq!(trim(&rows), vec!["1".to_string(), "1".to_string()]);141 assert_eq!(trim(&vec!["00000".to_string(); 5]), vec!["0"; 5]);142 }143}