1#![no_std]
2
3use core::fmt::Debug;
4
5pub const BIN_WIDTH: &'static [u8] = include_bytes!("unifont.width.bin");
6pub const BIN_BYTES: &'static [u8] = include_bytes!("unifont.bin");
7#[cfg(feature = "otf")]
8pub const OTF_BYTES: &'static [u8] = include_bytes!("unifont.otf");
9#[cfg(feature = "hex")]
10pub const HEX_BYTES: &'static [u8] = include_bytes!("unifont.hex");
11
12pub fn char_width(c: char) -> Option<usize> {
13 let code = c as u32;
14 if code < 65536 {
15 let width = BIN_WIDTH[code as usize] as usize;
16 if width == 0 { None } else { Some(width) }
17 } else {
18 None
19 }
20}
21
22pub fn get_glyph(c: char) -> Option<Unifont> {
23 let code = c as u32;
24 if code < 65536 {
25 let width = BIN_WIDTH[code as usize] as usize;
26 if width == 0 {
27 None
28 } else {
29 let data = &BIN_BYTES[code as usize * 32..code as usize * 32 + 32];
30 if width == 1 {
31 Some(Unifont {
32 inner: UnifontInner::HalfWidth(data),
33 })
34 } else {
35 Some(Unifont {
36 inner: UnifontInner::FullWidth(data),
37 })
38 }
39 }
40 } else {
41 None
42 }
43}
44
45enum UnifontInner {
46 HalfWidth(&'static [u8]),
47 FullWidth(&'static [u8]),
48}
49
50impl Debug for UnifontInner {
51 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52 let (width, data) = match self {
53 UnifontInner::HalfWidth(data) => (8, data),
54 UnifontInner::FullWidth(data) => (16, data),
55 };
56 match self {
57 UnifontInner::HalfWidth(_) => writeln!(f, "Unifont(HalfWidth(")?,
58 UnifontInner::FullWidth(_) => writeln!(f, "Unifont(FullWidth(")?,
59 }
60 for y in 0..16 {
61 write!(f, " ")?;
62 for x in 0..width {
63 let c = match (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 {
64 1 => '#',
65 _ => '.',
66 };
67 write!(f, "{}", c)?;
68 }
69 writeln!(f)?;
70 }
71 writeln!(f, "))")?;
72 Ok(())
73 }
74}
75
76pub struct Unifont {
77 inner: UnifontInner,
78}
79
80impl Debug for Unifont {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 self.inner.fmt(f)
83 }
84}
85
86impl Default for Unifont {
87 fn default() -> Self {
88 Unifont {
89 inner: UnifontInner::HalfWidth(&BIN_BYTES[32 * 32..33 * 32]),
90 }
91 }
92}
93
94impl Unifont {
95 pub fn width(&self) -> usize {
96 match self.inner {
97 UnifontInner::HalfWidth(_) => 8,
98 UnifontInner::FullWidth(_) => 16,
99 }
100 }
101
102 pub fn height(&self) -> usize {
103 16
104 }
105
106 pub fn get(&self, x: usize, y: usize) -> bool {
107 match self.inner {
108 UnifontInner::HalfWidth(data) => {
109 if x >= 8 || y >= 16 {
110 false
111 } else {
112 (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 == 1
113 }
114 }
115 UnifontInner::FullWidth(data) => {
116 if x >= 16 || y >= 16 {
117 false
118 } else {
119 (data[y * 2 + x / 8] >> (7 - x % 8)) & 1 == 1
120 }
121 }
122 }
123 }
124
125 pub unsafe fn get_unchecked(&self, x: usize, y: usize) -> bool {
126 unsafe {
127 match self.inner {
128 UnifontInner::HalfWidth(data) | UnifontInner::FullWidth(data) => {
129 (data.get_unchecked(y * 2 + x / 8) >> (7 - x % 8)) & 1 == 1
130 }
131 }
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_char_width() {
142 assert_eq!(char_width('A'), Some(1));
143 assert_eq!(char_width('あ'), Some(2));
144 assert_eq!(char_width('\u{E000}'), None);
145 assert_eq!(char_width('\u{10FFFF}'), None);
146 }
147
148 #[test]
149 fn test_get_glyph() {
150 let glyph_a = get_glyph('A').unwrap();
151 assert_eq!(glyph_a.width(), 8);
152 assert_eq!(glyph_a.height(), 16);
153
154 let glyph_あ = get_glyph('あ').unwrap();
155 assert_eq!(glyph_あ.width(), 16);
156 assert_eq!(glyph_あ.height(), 16);
157
158 assert!(get_glyph('\u{E000}').is_none());
159 assert!(get_glyph('\u{10FFFF}').is_none());
160 }
161}