1#[derive(Debug, Clone, Copy)]
34pub struct BitmapFont {
35 data: &'static [u8],
37 pub glyph_width: u8,
39 pub glyph_height: u8,
41 glyph_count: u16,
43 charset: Option<&'static [(char, u8)]>,
51}
52
53impl BitmapFont {
54 #[must_use]
59 pub const fn new(
60 data: &'static [u8],
61 glyph_width: u8,
62 glyph_height: u8,
63 glyph_count: u16,
64 ) -> Self {
65 Self {
66 data,
67 glyph_width,
68 glyph_height,
69 glyph_count,
70 charset: None,
71 }
72 }
73
74 #[must_use]
86 pub const fn with_charset(
87 data: &'static [u8],
88 glyph_width: u8,
89 glyph_height: u8,
90 glyph_count: u16,
91 charset: &'static [(char, u8)],
92 ) -> Self {
93 Self {
94 data,
95 glyph_width,
96 glyph_height,
97 glyph_count,
98 charset: Some(charset),
99 }
100 }
101
102 #[must_use]
110 pub fn rows(&self, index: u8) -> &[u8] {
111 assert!(
112 u16::from(index) < self.glyph_count,
113 "glyph index {index} out of range ({})",
114 self.glyph_count,
115 );
116 let h = usize::from(self.glyph_height);
117 let start = usize::from(index) * h;
118 &self.data[start..start + h]
119 }
120
121 #[must_use = "iterators are lazy and do nothing unless consumed"]
135 pub fn glyph_pixels(&self, index: u8) -> impl Iterator<Item = (u8, u8)> + '_ {
136 let width = self.glyph_width;
137 self.rows(index)
138 .iter()
139 .enumerate()
140 .flat_map(move |(y, &row)| {
141 #[allow(clippy::cast_possible_truncation)]
142 let y = y as u8;
143 (0..width)
144 .filter_map(move |x| ((row >> (width - 1 - x)) & 1 == 1).then_some((x, y)))
145 })
146 }
147
148 #[must_use]
153 pub const fn glyph_count(&self) -> u16 {
154 self.glyph_count
155 }
156
157 #[must_use]
172 pub const fn glyph_index(&self, ch: char) -> Option<u8> {
173 if let Some(table) = self.charset {
174 let mut i = 0;
175 while i < table.len() {
176 let (table_ch, index) = table[i];
177 if table_ch == ch && (index as u16) < self.glyph_count {
178 return Some(index);
179 }
180 i += 1;
181 }
182 return None;
183 }
184 match try_unicode_to_cp437(ch) {
185 Some(index) if (index as u16) < self.glyph_count => Some(index),
186 _ => None,
187 }
188 }
189}
190
191impl PartialEq for BitmapFont {
195 fn eq(&self, other: &Self) -> bool {
196 core::ptr::eq(self.data.as_ptr(), other.data.as_ptr())
197 && self.glyph_width == other.glyph_width
198 && self.glyph_height == other.glyph_height
199 && self.glyph_count == other.glyph_count
200 }
201}
202
203impl Eq for BitmapFont {}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub struct ResolvedGlyph {
211 font: BitmapFont,
212 font_index: usize,
213 index: u8,
214 notdef: bool,
215}
216
217impl ResolvedGlyph {
218 #[must_use]
220 pub const fn font(&self) -> &BitmapFont {
221 &self.font
222 }
223
224 #[must_use]
231 pub const fn font_index(&self) -> usize {
232 self.font_index
233 }
234
235 #[must_use]
237 pub const fn index(&self) -> u8 {
238 self.index
239 }
240
241 #[must_use]
245 pub const fn is_notdef(&self) -> bool {
246 self.notdef
247 }
248
249 #[must_use]
251 pub fn rows(&self) -> &[u8] {
252 self.font.rows(self.index)
253 }
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303pub struct FontChain<'a> {
304 primary: BitmapFont,
305 fallbacks: &'a [BitmapFont],
306}
307
308impl From<BitmapFont> for FontChain<'static> {
309 fn from(font: BitmapFont) -> Self {
310 Self::new(font, &[])
311 }
312}
313
314impl<'a> FontChain<'a> {
315 #[must_use]
317 pub const fn new(primary: BitmapFont, fallbacks: &'a [BitmapFont]) -> Self {
318 Self { primary, fallbacks }
319 }
320
321 pub fn fonts(&self) -> impl Iterator<Item = &BitmapFont> {
325 core::iter::once(&self.primary).chain(self.fallbacks.iter())
326 }
327
328 #[must_use]
330 pub const fn font_count(&self) -> usize {
331 1 + self.fallbacks.len()
332 }
333
334 #[must_use]
341 pub fn glyph_size(&self) -> Option<(u8, u8)> {
342 let size = (self.primary.glyph_width, self.primary.glyph_height);
343 self.fallbacks
344 .iter()
345 .all(|f| (f.glyph_width, f.glyph_height) == size)
346 .then_some(size)
347 }
348
349 #[must_use]
361 pub fn resolve(&self, ch: char) -> Option<ResolvedGlyph> {
362 self.lookup(ch, false).or_else(|| self.lookup(NOTDEF, true))
363 }
364
365 fn lookup(&self, ch: char, notdef: bool) -> Option<ResolvedGlyph> {
367 self.fonts()
368 .enumerate()
369 .find_map(|(font_index, font)| {
370 font.glyph_index(ch).map(|index| (font_index, font, index))
371 })
372 .map(|(font_index, font, index)| ResolvedGlyph {
373 font: *font,
374 font_index,
375 index,
376 notdef,
377 })
378 }
379}
380
381#[cfg(feature = "default-font")]
401pub mod unscii16 {
402 use super::BitmapFont;
403
404 pub const FONT: BitmapFont = BitmapFont::new(&DATA, 8, 16, 256);
406
407 #[rustfmt::skip]
411 static DATA: [u8; 4096] = [
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x54, 0xfe, 0xfe, 0x54, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x38, 0x38, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3c, 0x3c, 0x66, 0x66, 0x42, 0x42, 0x42, 0x42, 0x66, 0x66, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xc3, 0xc3, 0x99, 0x99, 0xbd, 0xbd, 0xbd, 0xbd, 0x99, 0x99, 0xc3, 0xc3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x1e, 0x1b, 0x18, 0x18, 0x78, 0xf8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0xbd, 0x7e, 0x7e, 0xbd, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xfc, 0xff, 0xfc, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x3f, 0xff, 0x3f, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x7a, 0x7a, 0x7a, 0x7a, 0x3a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x30, 0x38, 0x6c, 0x66, 0x36, 0x1c, 0x0c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0c, 0xfe, 0xfe, 0x0c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x06, 0xc6, 0xcc, 0xcc, 0x18, 0x18, 0x30, 0x30, 0x66, 0x66, 0xc6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x30, 0x7a, 0xde, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xce, 0xd6, 0xe6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x06, 0x1c, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x76, 0x3c, 0x6e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x06, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xee, 0xee, 0xfe, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0xee, 0xee, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x30, 0x30, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x78, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0x7c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x30, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x1c, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x1c, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x3c, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, 0x7f, 0xd8, 0xd8, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x7c, 0xfc, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x66, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x60, 0x60, 0xf0, 0x60, 0x60, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x30, 0x30, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc6, 0x46, 0x4c, 0x4c, 0x18, 0x18, 0x30, 0x30, 0x6c, 0x62, 0xc4, 0xc8, 0x0e, 0x00, 0x00, 0x40, 0xc6, 0x46, 0x4c, 0x4c, 0x18, 0x18, 0x30, 0x30, 0x62, 0x66, 0xca, 0xcf, 0x02, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x7c, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0x7c, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xee, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdb, 0xdb, 0xdb, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0xdc, 0xd6, 0xd6, 0xd6, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x72, 0xd6, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0xcc, 0xcc, 0x6c, 0x38, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
669}
670
671#[cfg(feature = "legacy-computing")]
712pub mod legacy_computing {
713 pub mod blocks {
720 use crate::font::BitmapFont;
721
722 const QUADRANT_COUNT: usize = 10;
724 const SEXTANT_COUNT: usize = 60;
726 const TOTAL: usize = QUADRANT_COUNT + SEXTANT_COUNT;
728
729 #[allow(clippy::cast_possible_truncation)]
735 pub const FONT: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, TOTAL as u16, &CHARSET);
736
737 #[rustfmt::skip]
744 const QUADRANTS: [(u8, char); QUADRANT_COUNT] = [
745 (1, '▘'), (2, '▝'), (4, '▖'), (6, '▞'), (7, '▛'),
746 (8, '▗'), (9, '▚'), (11, '▜'), (13, '▙'), (14, '▟'),
747 ];
748
749 const fn sextant_masks() -> [u8; SEXTANT_COUNT] {
757 let mut masks = [0u8; SEXTANT_COUNT];
758 let mut m: u16 = 1;
759 let mut i = 0;
760 while m <= 62 {
761 if m != 21 && m != 42 {
762 #[allow(clippy::cast_possible_truncation)]
763 {
764 masks[i] = m as u8;
765 }
766 i += 1;
767 }
768 m += 1;
769 }
770 masks
771 }
772
773 const fn sextant_codepoint(mask: u8) -> u32 {
781 let gaps_below = if mask > 21 { 1 } else { 0 } + if mask > 42 { 1 } else { 0 };
782 0x1_FB00 + (mask as u32 - 1) - gaps_below
783 }
784
785 const fn set_pixel(data: &mut [u8; TOTAL * 16], index: usize, x: u8, y: u8) {
788 let row = index * 16 + y as usize;
789 data[row] |= 1 << (7 - x);
790 }
791
792 const fn build_data() -> [u8; TOTAL * 16] {
795 let mut data = [0u8; TOTAL * 16];
796
797 let mut qi = 0;
800 while qi < QUADRANT_COUNT {
801 let (mask, _) = QUADRANTS[qi];
802 let mut y = 0u8;
803 while y < 16 {
804 let mut x = 0u8;
805 while x < 8 {
806 let bit = if x < 4 {
807 if y < 8 { 0 } else { 2 }
808 } else if y < 8 {
809 1
810 } else {
811 3
812 };
813 if (mask >> bit) & 1 == 1 {
814 set_pixel(&mut data, qi, x, y);
815 }
816 x += 1;
817 }
818 y += 1;
819 }
820 qi += 1;
821 }
822
823 let masks = sextant_masks();
826 let mut si = 0;
827 while si < SEXTANT_COUNT {
828 let mask = masks[si];
829 let index = QUADRANT_COUNT + si;
830 let mut y = 0u8;
831 while y < 16 {
832 let row = if y < 5 {
833 0
834 } else if y < 11 {
835 1
836 } else {
837 2
838 };
839 let mut x = 0u8;
840 while x < 8 {
841 let col: usize = if x >= 4 { 1 } else { 0 };
842 let bit = row * 2 + col;
843 if (mask >> bit) & 1 == 1 {
844 set_pixel(&mut data, index, x, y);
845 }
846 x += 1;
847 }
848 y += 1;
849 }
850 si += 1;
851 }
852
853 data
854 }
855
856 const fn build_charset() -> [(char, u8); TOTAL] {
859 let mut charset = [('\0', 0u8); TOTAL];
860
861 let mut qi = 0;
862 while qi < QUADRANT_COUNT {
863 let (_, ch) = QUADRANTS[qi];
864 #[allow(clippy::cast_possible_truncation)]
865 {
866 charset[qi] = (ch, qi as u8);
867 }
868 qi += 1;
869 }
870
871 let masks = sextant_masks();
872 let mut si = 0;
873 while si < SEXTANT_COUNT {
874 let cp = sextant_codepoint(masks[si]);
875 let Some(ch) = char::from_u32(cp) else {
876 panic!("sextant codepoint is not a valid char")
877 };
878 let index = QUADRANT_COUNT + si;
879 #[allow(clippy::cast_possible_truncation)]
880 {
881 charset[index] = (ch, index as u8);
882 }
883 si += 1;
884 }
885
886 charset
887 }
888
889 static DATA: [u8; TOTAL * 16] = build_data();
892
893 static CHARSET: [(char, u8); TOTAL] = build_charset();
895
896 #[cfg(test)]
897 mod tests {
898 use super::{CHARSET, FONT, SEXTANT_COUNT, TOTAL, sextant_codepoint, sextant_masks};
899 use crate::font::FontChain;
900 use std::collections::HashSet;
901
902 #[test]
903 fn total_glyph_count_matches_quadrants_plus_sextants() {
904 assert_eq!(TOTAL, 10 + 60);
905 assert_eq!(FONT.glyph_count(), u16::try_from(TOTAL).unwrap());
906 }
907
908 #[test]
909 fn no_charset_entry_duplicates_a_codepoint_cp437_already_serves() {
910 let already_cp437: HashSet<char> = [' ', '▀', '▄', '▌', '▐', '█', '░', '▒', '▓']
914 .into_iter()
915 .collect();
916 for &(ch, _) in &CHARSET {
917 assert!(
918 !already_cp437.contains(&ch),
919 "{ch:?} (U+{:04X}) duplicates existing CP437 coverage",
920 ch as u32
921 );
922 }
923 }
924
925 #[test]
926 fn every_charset_character_appears_exactly_once() {
927 let mut seen = HashSet::with_capacity(TOTAL);
928 for &(ch, _) in &CHARSET {
929 assert!(seen.insert(ch), "{ch:?} appears more than once in CHARSET");
930 }
931 assert_eq!(seen.len(), TOTAL);
932 }
933
934 #[test]
935 fn sextant_masks_skip_21_and_42() {
936 let masks = sextant_masks();
937 assert_eq!(masks.len(), SEXTANT_COUNT);
938 assert!(!masks.contains(&21));
939 assert!(!masks.contains(&42));
940 assert_eq!(masks[0], 1);
941 assert_eq!(masks[SEXTANT_COUNT - 1], 62);
942 }
943
944 #[test]
945 fn sextant_codepoint_shifts_down_after_each_gap() {
946 assert_eq!(sextant_codepoint(1), 0x1FB00);
947 assert_eq!(sextant_codepoint(22), 0x1FB00 + 21 - 1);
950 assert_eq!(sextant_codepoint(43), 0x1FB00 + 42 - 2);
952 }
953
954 #[test]
956 fn quadrant_top_left_mask_only_fills_the_top_left_quarter() {
957 let index = FONT.glyph_index('▘').expect("U+2598 is covered");
958 for (x, y) in FONT.glyph_pixels(index) {
959 assert!(x < 4 && y < 8, "({x}, {y}) outside the top-left quarter");
960 }
961 }
962
963 #[test]
964 fn font_chain_resolves_quadrant_via_blocks_fallback() {
965 static PRIMARY_DATA: [u8; 256 * 16] = [0; 256 * 16];
966 const PRIMARY: crate::font::BitmapFont =
967 crate::font::BitmapFont::new(&PRIMARY_DATA, 8, 16, 256);
968
969 static FALLBACKS: [crate::font::BitmapFont; 1] = [FONT];
970 let chain = FontChain::new(PRIMARY, &FALLBACKS);
971
972 let quadrant = chain
973 .resolve('▘')
974 .expect("covered by legacy_computing::blocks");
975 assert_eq!(quadrant.font_index(), 1);
976 assert!(!quadrant.is_notdef());
977 }
978 }
979 }
980
981 pub mod braille {
988 use crate::font::BitmapFont;
989
990 const TOTAL: usize = 256;
992
993 #[allow(clippy::cast_possible_truncation)]
999 pub const FONT: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, TOTAL as u16, &CHARSET);
1000
1001 const COL_X: [u8; 2] = [1, 4];
1003 const ROW_Y: [u8; 4] = [1, 5, 9, 13];
1005
1006 const fn bit_index(col: usize, row: usize) -> u32 {
1011 match (col, row) {
1012 (0, 0) => 0,
1013 (0, 1) => 1,
1014 (0, 2) => 2,
1015 (0, 3) => 6,
1016 (1, 0) => 3,
1017 (1, 1) => 4,
1018 (1, 2) => 5,
1019 (1, 3) => 7,
1020 _ => panic!("braille dot position out of range"),
1021 }
1022 }
1023
1024 const fn set_pixel(data: &mut [u8; TOTAL * 16], index: usize, x: u8, y: u8) {
1027 let row = index * 16 + y as usize;
1028 data[row] |= 1 << (7 - x);
1029 }
1030
1031 const fn build_data() -> [u8; TOTAL * 16] {
1034 #[allow(clippy::cast_possible_truncation)]
1035 const TOTAL_U32: u32 = TOTAL as u32;
1036
1037 let mut data = [0u8; TOTAL * 16];
1038
1039 let mut bits: u32 = 0;
1040 while bits < TOTAL_U32 {
1041 let index = bits as usize;
1042 let mut col = 0usize;
1043 while col < 2 {
1044 let mut row = 0usize;
1045 while row < 4 {
1046 let bit = bit_index(col, row);
1047 if (bits >> bit) & 1 == 1 {
1048 let cx = COL_X[col];
1049 let cy = ROW_Y[row];
1050 let mut dy: i32 = -1;
1051 while dy <= 1 {
1052 let mut dx: i32 = -1;
1053 while dx <= 1 {
1054 let px = cx as i32 + dx;
1055 let py = cy as i32 + dy;
1056 if px >= 0 && px < 8 && py >= 0 && py < 16 {
1057 #[allow(
1058 clippy::cast_sign_loss,
1059 clippy::cast_possible_truncation
1060 )]
1061 set_pixel(&mut data, index, px as u8, py as u8);
1062 }
1063 dx += 1;
1064 }
1065 dy += 1;
1066 }
1067 }
1068 row += 1;
1069 }
1070 col += 1;
1071 }
1072 bits += 1;
1073 }
1074
1075 data
1076 }
1077
1078 const fn build_charset() -> [(char, u8); TOTAL] {
1081 #[allow(clippy::cast_possible_truncation)]
1082 const TOTAL_U32: u32 = TOTAL as u32;
1083
1084 let mut charset = [('\0', 0u8); TOTAL];
1085
1086 let mut bits: u32 = 0;
1087 while bits < TOTAL_U32 {
1088 let cp = 0x2800 + bits;
1089 let Some(ch) = char::from_u32(cp) else {
1090 panic!("braille codepoint is not a valid char")
1091 };
1092 let index = bits as usize;
1093 #[allow(clippy::cast_possible_truncation)]
1094 {
1095 charset[index] = (ch, index as u8);
1096 }
1097 bits += 1;
1098 }
1099
1100 charset
1101 }
1102
1103 static DATA: [u8; TOTAL * 16] = build_data();
1106
1107 static CHARSET: [(char, u8); TOTAL] = build_charset();
1109
1110 #[cfg(test)]
1111 mod tests {
1112 use super::{CHARSET, FONT, TOTAL};
1113 use crate::font::FontChain;
1114 use std::collections::HashSet;
1115
1116 #[test]
1117 fn total_glyph_count_is_256() {
1118 assert_eq!(TOTAL, 256);
1119 assert_eq!(FONT.glyph_count(), 256);
1120 }
1121
1122 #[test]
1123 fn every_charset_character_appears_exactly_once() {
1124 let mut seen = HashSet::with_capacity(TOTAL);
1125 for &(ch, _) in &CHARSET {
1126 assert!(seen.insert(ch), "{ch:?} appears more than once in CHARSET");
1127 }
1128 assert_eq!(seen.len(), TOTAL);
1129 }
1130
1131 #[test]
1132 fn covers_the_full_u2800_block() {
1133 for bits in 0u32..u32::try_from(TOTAL).unwrap() {
1134 let ch = char::from_u32(0x2800 + bits).unwrap();
1135 assert_eq!(CHARSET[bits as usize].0, ch);
1136 assert_eq!(CHARSET[bits as usize].1, u8::try_from(bits).unwrap());
1137 }
1138 }
1139
1140 #[test]
1141 fn blank_glyph_is_all_zero_bits() {
1142 let index = FONT.glyph_index('\u{2800}').expect("U+2800 is covered");
1143 assert!(FONT.rows(index).iter().all(|&b| b == 0));
1144 }
1145
1146 #[test]
1147 fn full_glyph_has_all_dot_positions_set() {
1148 let index = FONT.glyph_index('\u{28FF}').expect("U+28FF is covered");
1149 let pixel_count = FONT.glyph_pixels(index).count();
1150 assert_eq!(pixel_count, 72);
1153 }
1154
1155 #[test]
1158 fn font_chain_resolves_via_braille_fallback() {
1159 static PRIMARY_DATA: [u8; 256 * 16] = [0; 256 * 16];
1160 const PRIMARY: crate::font::BitmapFont =
1161 crate::font::BitmapFont::new(&PRIMARY_DATA, 8, 16, 256);
1162
1163 static FALLBACKS: [crate::font::BitmapFont; 1] = [FONT];
1164 let chain = FontChain::new(PRIMARY, &FALLBACKS);
1165
1166 let braille = chain
1168 .resolve('\u{2837}')
1169 .expect("covered by legacy_computing::braille");
1170 assert_eq!(braille.font_index(), 1);
1171 assert!(!braille.is_notdef());
1172
1173 let full_block = chain.resolve('█').expect("CP437 coverage");
1176 assert_eq!(*full_block.font(), PRIMARY);
1177 assert!(!full_block.is_notdef());
1178 }
1179 }
1180 }
1181}
1182
1183const NOTDEF: char = '█';
1192
1193#[allow(clippy::too_many_lines)]
1202const fn try_unicode_to_cp437(ch: char) -> Option<u8> {
1203 let u = ch as u32;
1205 if u < 0x80 {
1206 #[allow(clippy::cast_possible_truncation)]
1207 return Some(u as u8);
1208 }
1209
1210 match ch {
1212 'Ç' => Some(0x80),
1214 'ü' => Some(0x81),
1215 'é' => Some(0x82),
1216 'â' => Some(0x83),
1217 'ä' => Some(0x84),
1218 'à' => Some(0x85),
1219 'å' => Some(0x86),
1220 'ç' => Some(0x87),
1221 'ê' => Some(0x88),
1222 'ë' => Some(0x89),
1223 'è' => Some(0x8A),
1224 'ï' => Some(0x8B),
1225 'î' => Some(0x8C),
1226 'ì' => Some(0x8D),
1227 'Ä' => Some(0x8E),
1228 'Å' => Some(0x8F),
1229 'É' => Some(0x90),
1230 'æ' => Some(0x91),
1231 'Æ' => Some(0x92),
1232 'ô' => Some(0x93),
1233 'ö' => Some(0x94),
1234 'ò' => Some(0x95),
1235 'û' => Some(0x96),
1236 'ù' => Some(0x97),
1237 'ÿ' => Some(0x98),
1238 'Ö' => Some(0x99),
1239 'Ü' => Some(0x9A),
1240 '¢' => Some(0x9B),
1241 '£' => Some(0x9C),
1242 '¥' => Some(0x9D),
1243 'ƒ' => Some(0x9F),
1244 'á' => Some(0xA0),
1245 'í' => Some(0xA1),
1246 'ó' => Some(0xA2),
1247 'ú' => Some(0xA3),
1248 'ñ' => Some(0xA4),
1249 'Ñ' => Some(0xA5),
1250 'ª' => Some(0xA6),
1251 'º' => Some(0xA7),
1252 '¿' => Some(0xA8),
1253 '⌐' => Some(0xA9),
1254 '½' => Some(0xAB),
1255 '¼' => Some(0xAC),
1256 '¡' => Some(0xAD),
1257 '«' => Some(0xAE),
1258 '»' => Some(0xAF),
1259
1260 '░' => Some(0xB0),
1262 '▒' => Some(0xB1),
1263 '▓' => Some(0xB2),
1264
1265 '│' => Some(0xB3),
1267 '┤' => Some(0xB4),
1268 '╡' => Some(0xB5),
1269 '╢' => Some(0xB6),
1270 '╖' => Some(0xB7),
1271 '╕' => Some(0xB8),
1272 '╣' => Some(0xB9),
1273 '║' => Some(0xBA),
1274 '╗' => Some(0xBB),
1275 '╝' => Some(0xBC),
1276 '╜' => Some(0xBD),
1277 '╛' => Some(0xBE),
1278 '┐' => Some(0xBF),
1279 '└' => Some(0xC0),
1280 '┴' => Some(0xC1),
1281 '┬' => Some(0xC2),
1282 '├' => Some(0xC3),
1283 '─' => Some(0xC4),
1284 '┼' => Some(0xC5),
1285 '╞' => Some(0xC6),
1286 '╟' => Some(0xC7),
1287 '╚' => Some(0xC8),
1288 '╔' => Some(0xC9),
1289 '╩' => Some(0xCA),
1290 '╦' => Some(0xCB),
1291 '╠' => Some(0xCC),
1292 '═' => Some(0xCD),
1293 '╬' => Some(0xCE),
1294 '╧' => Some(0xCF),
1295 '╨' => Some(0xD0),
1296 '╤' => Some(0xD1),
1297 '╥' => Some(0xD2),
1298 '╙' => Some(0xD3),
1299 '╘' => Some(0xD4),
1300 '╒' => Some(0xD5),
1301 '╓' => Some(0xD6),
1302 '╫' => Some(0xD7),
1303 '╪' => Some(0xD8),
1304 '┘' => Some(0xD9),
1305 '┌' => Some(0xDA),
1306
1307 '█' => Some(0xDB),
1309 '▄' => Some(0xDC),
1310 '▌' => Some(0xDD),
1311 '▐' => Some(0xDE),
1312 '▀' => Some(0xDF),
1313
1314 'α' => Some(0xE0),
1316 'ß' => Some(0xE1),
1317 'Γ' => Some(0xE2),
1318 'π' => Some(0xE3),
1319 'Σ' => Some(0xE4),
1320 'σ' => Some(0xE5),
1321 'µ' | 'μ' => Some(0xE6),
1322 'τ' => Some(0xE7),
1323 'Φ' => Some(0xE8),
1324 'Θ' => Some(0xE9),
1325 'Ω' => Some(0xEA),
1326 'δ' => Some(0xEB),
1327 '∞' => Some(0xEC),
1328 'φ' => Some(0xED),
1329 'ε' => Some(0xEE),
1330 '∩' => Some(0xEF),
1331 '≡' => Some(0xF0),
1332 '±' => Some(0xF1),
1333 '≥' => Some(0xF2),
1334 '≤' => Some(0xF3),
1335 '⌠' => Some(0xF4),
1336 '⌡' => Some(0xF5),
1337 '÷' => Some(0xF6),
1338 '≈' => Some(0xF7),
1339 '°' => Some(0xF8),
1340 '·' | '∙' => Some(0xF9),
1341 '√' => Some(0xFB),
1342 'ⁿ' => Some(0xFC),
1343 '²' => Some(0xFD),
1344 '■' => Some(0xFE),
1345
1346 '☺' => Some(0x01),
1348 '•' => Some(0x07),
1349 '☻' => Some(0x02),
1350 '♥' => Some(0x03),
1351 '♦' => Some(0x04),
1352 '♣' => Some(0x05),
1353 '♠' => Some(0x06),
1354 '◘' => Some(0x08),
1355 '○' => Some(0x09),
1356 '◙' => Some(0x0A),
1357 '♂' => Some(0x0B),
1358 '♀' => Some(0x0C),
1359 '♪' => Some(0x0D),
1360 '♫' => Some(0x0E),
1361 '☼' => Some(0x0F),
1362 '►' => Some(0x10),
1363 '◄' => Some(0x11),
1364 '↕' => Some(0x12),
1365 '‼' => Some(0x13),
1366 '¶' => Some(0x14),
1367 '§' => Some(0x15),
1368 '▬' => Some(0x16),
1369 '↨' => Some(0x17),
1370 '↑' => Some(0x18),
1371 '↓' => Some(0x19),
1372 '→' => Some(0x1A),
1373 '←' => Some(0x1B),
1374 '∟' => Some(0x1C),
1375 '↔' => Some(0x1D),
1376 '▲' => Some(0x1E),
1377 '▼' => Some(0x1F),
1378 '⌂' => Some(0x7F),
1379
1380 _ => None,
1381 }
1382}
1383
1384#[cfg(test)]
1385mod tests {
1386 use super::{BitmapFont, FontChain, try_unicode_to_cp437};
1387
1388 #[test]
1393 fn patched_glyphs_are_reachable_by_char() {
1394 assert_eq!(try_unicode_to_cp437('⌂'), Some(0x7F), "U+2302 HOUSE");
1395 assert_eq!(
1396 try_unicode_to_cp437('☼'),
1397 Some(0x0F),
1398 "U+263C WHITE SUN WITH RAYS"
1399 );
1400 assert_eq!(
1401 try_unicode_to_cp437('⌐'),
1402 Some(0xA9),
1403 "U+2310 REVERSED NOT SIGN"
1404 );
1405 assert_eq!(
1406 try_unicode_to_cp437('∙'),
1407 Some(0xF9),
1408 "U+2219 BULLET OPERATOR"
1409 );
1410 }
1411
1412 static PRIMARY_DATA: [u8; 128 * 16] = [0; 128 * 16];
1415 const PRIMARY: BitmapFont = BitmapFont::new(&PRIMARY_DATA, 8, 16, 128);
1416
1417 static FALLBACK_DATA: [u8; 256 * 16] = [0; 256 * 16];
1419 const FALLBACK_FONT: BitmapFont = BitmapFont::new(&FALLBACK_DATA, 8, 16, 256);
1420
1421 #[test]
1422 fn chain_resolves_char_present_only_in_fallback_font() {
1423 let chain = FontChain::new(PRIMARY, &[FALLBACK_FONT]);
1426 let resolved = chain.resolve('Ç').expect("covered by the fallback font");
1427 assert_eq!(*resolved.font(), FALLBACK_FONT);
1428 assert_eq!(resolved.font_index(), 1);
1429 assert_eq!(resolved.index(), 0x80);
1430 assert!(!resolved.is_notdef());
1431 }
1432
1433 #[test]
1434 fn chain_falls_back_to_solid_block_when_every_font_misses() {
1435 let chain = FontChain::new(PRIMARY, &[FALLBACK_FONT]);
1440 let resolved = chain.resolve('あ').expect("solid block substitute");
1441 assert_eq!(*resolved.font(), FALLBACK_FONT);
1442 assert_eq!(resolved.index(), 0xDB);
1443 assert!(resolved.is_notdef());
1444 }
1445
1446 #[test]
1447 fn chain_resolves_nothing_when_no_font_has_a_substitute() {
1448 static DATA: [u8; 16] = [0; 16];
1452 const CHARSET: [(char, u8); 1] = [('\u{2800}', 0)];
1453 const BRAILLE: BitmapFont = BitmapFont::with_charset(&DATA, 8, 16, 1, &CHARSET);
1454
1455 let chain = FontChain::new(BRAILLE, &[]);
1456 assert!(chain.resolve('\u{2800}').is_some());
1457 assert!(chain.resolve('A').is_none());
1458 }
1459
1460 #[test]
1461 fn single_font_chain_resolves_that_font_directly() {
1462 let chain = FontChain::from(FALLBACK_FONT);
1463 assert_eq!(chain.font_count(), 1);
1464 for ch in ['A', ' ', '█', '│', 'Ç', '☺'] {
1465 let resolved = chain.resolve(ch).expect("CP437 coverage");
1466 assert_eq!(*resolved.font(), FALLBACK_FONT);
1467 assert_eq!(resolved.font_index(), 0);
1468 assert_eq!(resolved.index(), FALLBACK_FONT.glyph_index(ch).unwrap());
1469 }
1470 }
1471
1472 #[test]
1473 fn glyph_size_is_none_for_a_chain_of_mismatched_fonts() {
1474 static DATA: [u8; 8] = [0; 8];
1475 const SHORT: BitmapFont = BitmapFont::new(&DATA, 8, 8, 1);
1476
1477 assert_eq!(
1478 FontChain::new(PRIMARY, &[FALLBACK_FONT]).glyph_size(),
1479 Some((8, 16))
1480 );
1481 assert_eq!(FontChain::new(PRIMARY, &[SHORT]).glyph_size(), None);
1482 }
1483
1484 #[test]
1485 fn glyph_pixels_decodes_msb_first_row_major() {
1486 static DATA: [u8; 4] = [0b1000_0001, 0b0000_0000, 0b1111_1111, 0b0000_1000];
1489 let font = BitmapFont::new(&DATA, 8, 2, 2);
1490
1491 let g0: Vec<(u8, u8)> = font.glyph_pixels(0).collect();
1492 assert_eq!(
1493 g0,
1494 [(0, 0), (7, 0)],
1495 "MSB is the leftmost pixel; row 0 first"
1496 );
1497
1498 let g1: Vec<(u8, u8)> = font.glyph_pixels(1).collect();
1499 let mut expected: Vec<(u8, u8)> = (0..8).map(|x| (x, 0)).collect();
1500 expected.push((4, 1)); assert_eq!(g1, expected);
1502 }
1503
1504 #[test]
1505 fn glyph_pixels_is_parameterized_by_width_not_hardcoded_to_8() {
1506 static DATA: [u8; 1] = [0b0001_0001];
1509 let font = BitmapFont::new(&DATA, 5, 1, 1);
1510 let pixels: Vec<(u8, u8)> = font.glyph_pixels(0).collect();
1511 assert_eq!(pixels, [(0, 0), (4, 0)]);
1512 }
1513
1514 #[test]
1520 fn chain_extends_past_cp437_via_charset_fallback_font() {
1521 static BRAILLE_DATA: [u8; 16] = [0; 16];
1522 const BRAILLE_CHARSET: [(char, u8); 1] = [('\u{2800}', 0)];
1523 const BRAILLE_FONT: BitmapFont =
1524 BitmapFont::with_charset(&BRAILLE_DATA, 8, 16, 1, &BRAILLE_CHARSET);
1525
1526 let primary = FALLBACK_FONT; let chain = FontChain::new(primary, &[BRAILLE_FONT]);
1528
1529 let braille = chain.resolve('\u{2800}').expect("charset coverage");
1530 assert_eq!(*braille.font(), BRAILLE_FONT);
1531 assert_eq!(braille.index(), 0);
1532
1533 let full_block = chain.resolve('\u{2588}').expect("CP437 coverage"); assert_eq!(*full_block.font(), primary);
1535 assert_eq!(full_block.index(), 0xDB);
1536
1537 assert_ne!(braille.index(), full_block.index());
1538 }
1539}