raster.rs
3.5 kB · rust · 131 lines
1use super::{descends, glyph, trim};23pub(crate) struct Block {4 /// The character the block draws.5 pub char: char,6 /// The glyph rows, top to bottom.7 pub rows: Vec<String>,8 /// The column the block starts at.9 pub col: usize,10 /// The rows the block sits below the baseline.11 pub offset: usize,12}1314impl Block {15 /// Reports the block width in columns.16 pub fn width(&self) -> usize {17 self.rows[0].len()18 }19}2021pub(crate) struct Layout {22 /// The height in rows.23 pub height: usize,24 /// The width in columns.25 pub width: usize,26 /// The placed blocks, in reading order.27 pub blocks: Vec<Block>,28}2930pub(crate) fn layout(text: &str) -> Layout {31 if text.is_empty() {32 return Layout {33 height: 0,34 width: 0,35 blocks: Vec::new(),36 };37 }38 let height = if text.chars().any(descends) { 7 } else { 5 };39 let shapes: Vec<(char, Vec<String>)> = text40 .chars()41 .map(|c| match glyph(c) {42 Some(g) if c != ' ' => (c, trim(&g.rows)),43 _ => (c, vec!["000".to_string(); 5]),44 })45 .collect();46 let width = shapes.iter().map(|s| s.1[0].len()).sum::<usize>() + shapes.len() - 1;47 let mut blocks = Vec::new();48 let mut col = 0;49 for (c, rows) in shapes {50 let offset = (height - rows.len()) / 2;51 let step = rows[0].len() + 1;52 blocks.push(Block {53 char: c,54 rows,55 col,56 offset,57 });58 col += step;59 }60 Layout {61 height,62 width,63 blocks,64 }65}6667/// Returns the text as a 0/1 grid, its trimmed glyphs one blank column apart.68///69/// ```70/// assert_eq!(mrlyfont::raster("42").len(), 5);71/// assert_eq!(mrlyfont::raster("(1)").len(), 7);72/// ```73pub fn raster(text: &str) -> Vec<Vec<u8>> {74 let laid = layout(text);75 if laid.blocks.is_empty() {76 return Vec::new();77 }78 let mut grid = vec![vec![0u8; laid.width]; laid.height];79 for block in &laid.blocks {80 for (r, row) in block.rows.iter().enumerate() {81 for (c, ch) in row.chars().enumerate() {82 if ch == '1' {83 grid[block.offset + r][block.col + c] = 1;84 }85 }86 }87 }88 grid89}9091#[cfg(test)]92mod tests {93 use super::*;9495 #[test]96 fn digits_string_is_five_tall_with_gapped_width() {97 let rows = raster("42");98 assert_eq!(rows.len(), 5);99 let w4 = trim(&glyph('4').unwrap().rows)[0].len();100 let w2 = trim(&glyph('2').unwrap().rows)[0].len();101 assert_eq!(rows[0].len(), w4 + w2 + 1);102 }103104 #[test]105 fn descender_makes_the_grid_seven_tall() {106 assert_eq!(raster("(1)").len(), 7);107 }108109 #[test]110 fn five_row_glyph_straddles_in_a_seven_row_grid() {111 let rows = raster("(1)");112 let w_paren = trim(&glyph('(').unwrap().rows)[0].len();113 let w_one = trim(&glyph('1').unwrap().rows)[0].len();114 let start = w_paren + 1;115 assert!(rows[0][start..start + w_one].iter().all(|&c| c == 0));116 assert!(rows[6][start..start + w_one].iter().all(|&c| c == 0));117 }118119 #[test]120 fn empty_text_is_empty() {121 assert_eq!(raster(""), Vec::<Vec<u8>>::new());122 }123124 #[test]125 fn unknown_chars_become_three_blank_columns() {126 let rows = raster("\u{00a7}");127 assert_eq!(rows.len(), 5);128 assert_eq!(rows[0].len(), 3);129 assert!(rows.iter().all(|row| row.iter().all(|&v| v == 0)));130 }131}