342 lines
8.0 KiB
Rust
342 lines
8.0 KiB
Rust
//! Extra glyphs the ISO-8859-15 render font lacks.
|
||
//!
|
||
//! The buffer is UTF-8, so it can hold codepoints outside Latin-9. The
|
||
//! `embedded-graphics` `iso_8859_15` font draws those as a fallback box. This
|
||
//! module carries hand-authored 10×20 bitmaps for a curated set of common
|
||
//! single-width characters — typographic punctuation that arrives in imported
|
||
//! prose (curly quotes, en/em dashes, ellipsis, bullet) and the math symbols the
|
||
//! user's Markdown snippets insert (→ ≠ Σ). [`Editor::draw`] overlays these over
|
||
//! the fallback box after the font has laid out the line.
|
||
//!
|
||
//! Every glyph is exactly one 10×20 cell (matching the body `FONT_10X20`, i.e.
|
||
//! `editor::CW`×`editor::CH`), so the editor's 1-char = 1-cell layout invariant
|
||
//! is untouched. A row is a 10-bit pattern: the leftmost column (x = 0) is the
|
||
//! high bit, so a binary literal reads left-to-right as the pixels of that row.
|
||
//! Rows are top (y = 0) to bottom (y = 19); 1 = ink.
|
||
|
||
use crate::Frame;
|
||
use embedded_graphics::pixelcolor::BinaryColor;
|
||
use embedded_graphics::prelude::*;
|
||
|
||
const GLYPH_W: usize = 10;
|
||
const GLYPH_H: usize = 20;
|
||
|
||
/// A 10×20 1-bit glyph: one `u16` per row, low `GLYPH_W` bits used, high bit =
|
||
/// leftmost pixel.
|
||
pub type Glyph = [u16; GLYPH_H];
|
||
|
||
// → U+2192 RIGHTWARDS ARROW: a horizontal shaft with a `>` head at the right.
|
||
#[rustfmt::skip]
|
||
const ARROW_RIGHT: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000010000,
|
||
0b0000001000,
|
||
0b0000000100,
|
||
0b0000000010,
|
||
0b0111111111,
|
||
0b0111111111,
|
||
0b0000000010,
|
||
0b0000000100,
|
||
0b0000001000,
|
||
0b0000010000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// ≠ U+2260 NOT EQUAL TO: two equals bars with a slash through them.
|
||
#[rustfmt::skip]
|
||
const NOT_EQUAL: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000010,
|
||
0b0000000100,
|
||
0b0000000100,
|
||
0b0000001000,
|
||
0b0111111110,
|
||
0b0000010000,
|
||
0b0000010000,
|
||
0b0000100000,
|
||
0b0111111110,
|
||
0b0001000000,
|
||
0b0001000000,
|
||
0b0010000000,
|
||
0b0010000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// Σ U+03A3 GREEK CAPITAL LETTER SIGMA: top & bottom bars with the diagonals
|
||
// meeting at a vertex on the centre-right.
|
||
#[rustfmt::skip]
|
||
const SIGMA: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0111111110,
|
||
0b0100000000,
|
||
0b0010000000,
|
||
0b0001000000,
|
||
0b0000100000,
|
||
0b0000010000,
|
||
0b0000100000,
|
||
0b0001000000,
|
||
0b0010000000,
|
||
0b0100000000,
|
||
0b0100000000,
|
||
0b0111111110,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// • U+2022 BULLET: a filled dot at mid-height.
|
||
#[rustfmt::skip]
|
||
const BULLET: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0001111000,
|
||
0b0011111100,
|
||
0b0011111100,
|
||
0b0011111100,
|
||
0b0001111000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// … U+2026 HORIZONTAL ELLIPSIS: three dots on the baseline.
|
||
#[rustfmt::skip]
|
||
const ELLIPSIS: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0110110110,
|
||
0b0110110110,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// – U+2013 EN DASH: a short mid-height bar.
|
||
#[rustfmt::skip]
|
||
const EN_DASH: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0011111100,
|
||
0b0011111100,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// — U+2014 EM DASH: a full-width mid-height bar.
|
||
#[rustfmt::skip]
|
||
const EM_DASH: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b1111111111,
|
||
0b1111111111,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// ' U+2018 LEFT SINGLE QUOTATION MARK: raised, tail up (opening).
|
||
#[rustfmt::skip]
|
||
const LEFT_SINGLE_QUOTE: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000100000,
|
||
0b0001100000,
|
||
0b0001100000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// ' U+2019 RIGHT SINGLE QUOTATION MARK / apostrophe: raised, tail down (closing).
|
||
#[rustfmt::skip]
|
||
const RIGHT_SINGLE_QUOTE: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0001100000,
|
||
0b0001100000,
|
||
0b0000100000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// " U+201C LEFT DOUBLE QUOTATION MARK: two opening quotes.
|
||
#[rustfmt::skip]
|
||
const LEFT_DOUBLE_QUOTE: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0010010000,
|
||
0b0011011000,
|
||
0b0011011000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
// " U+201D RIGHT DOUBLE QUOTATION MARK: two closing quotes.
|
||
#[rustfmt::skip]
|
||
const RIGHT_DOUBLE_QUOTE: Glyph = [
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0011011000,
|
||
0b0011011000,
|
||
0b0010010000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
0b0000000000,
|
||
];
|
||
|
||
/// The 10×20 bitmap for a character the ISO-8859-15 font can't draw, or `None`
|
||
/// if the base font already covers it (all ASCII and Latin-9, including `œ €`).
|
||
pub fn extra_glyph(c: char) -> Option<&'static Glyph> {
|
||
Some(match c {
|
||
'\u{2192}' => &ARROW_RIGHT, // →
|
||
'\u{2260}' => &NOT_EQUAL, // ≠
|
||
'\u{03A3}' => &SIGMA, // Σ
|
||
'\u{2022}' => &BULLET, // •
|
||
'\u{2026}' => &ELLIPSIS, // …
|
||
'\u{2013}' => &EN_DASH, // –
|
||
'\u{2014}' => &EM_DASH, // —
|
||
'\u{2018}' => &LEFT_SINGLE_QUOTE, // '
|
||
'\u{2019}' => &RIGHT_SINGLE_QUOTE, // '
|
||
'\u{201C}' => &LEFT_DOUBLE_QUOTE, // "
|
||
'\u{201D}' => &RIGHT_DOUBLE_QUOTE, // "
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
/// Paint a glyph over one 10×20 cell at `(x, y)`. Fills the whole cell — set
|
||
/// bits in `ink`, unset bits in `ink.invert()` — so it fully overwrites whatever
|
||
/// the base font drew there (e.g. the fallback box). Pass `ink = On` for a
|
||
/// normal black-on-white cell, `ink = Off` for a reverse-video cell (a white
|
||
/// glyph over the black selection/caret fill).
|
||
pub fn blit_glyph(f: &mut Frame, x: i32, y: i32, g: &Glyph, ink: BinaryColor) {
|
||
let bg = ink.invert();
|
||
let pixels = g.iter().enumerate().flat_map(move |(row, &bits)| {
|
||
(0..GLYPH_W).map(move |c| {
|
||
let on = (bits >> (GLYPH_W - 1 - c)) & 1 == 1;
|
||
Pixel(
|
||
Point::new(x + c as i32, y + row as i32),
|
||
if on { ink } else { bg },
|
||
)
|
||
})
|
||
});
|
||
// Frame's DrawTarget error is Infallible.
|
||
let _ = f.draw_iter(pixels);
|
||
}
|