Skip to main content

blockworx_paint/
font.rs

1use std::sync::Arc;
2
3/// The family a diagram glyph is drawn in. Repointed to the user's selected
4/// font when the fonts are installed, so switching fonts restyles the drawing
5/// and leaves the UI chrome alone.
6pub const CANVAS_FAMILY: &str = "Canvas";
7
8/// Which typeface to select, independent of size.
9#[derive(
10    Clone,
11    Debug,
12    Default,
13    Eq,
14    Hash,
15    Ord,
16    PartialEq,
17    PartialOrd,
18    serde::Serialize,
19    serde::Deserialize,
20)]
21pub enum FontFamily {
22    #[default]
23    Proportional,
24
25    Monospace,
26
27    /// One of the families the backend registered under a name.
28    Named(Arc<str>),
29}
30
31impl FontFamily {
32    /// The family every glyph on the canvas is drawn in.
33    pub fn canvas() -> Self {
34        Self::Named(CANVAS_FAMILY.into())
35    }
36}
37
38impl std::fmt::Display for FontFamily {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            Self::Proportional => "Proportional".fmt(f),
42            Self::Monospace => "Monospace".fmt(f),
43            Self::Named(name) => (**name).fmt(f),
44        }
45    }
46}
47
48/// A sized font selection.
49#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
50pub struct Font {
51    /// Height in points.
52    pub size: f32,
53
54    pub family: FontFamily,
55}
56
57impl Font {
58    #[inline]
59    pub const fn new(size: f32, family: FontFamily) -> Self {
60        Self { size, family }
61    }
62
63    #[inline]
64    pub const fn proportional(size: f32) -> Self {
65        Self::new(size, FontFamily::Proportional)
66    }
67
68    #[inline]
69    pub const fn monospace(size: f32) -> Self {
70        Self::new(size, FontFamily::Monospace)
71    }
72
73    /// A canvas glyph at `size` — see [`CANVAS_FAMILY`].
74    pub fn canvas(size: f32) -> Self {
75        Self::new(size, FontFamily::canvas())
76    }
77}
78
79// These are `static`, not `const`, on purpose: a `const` is inlined into every
80// codegen unit that names it, and the SVG glyph tracer is a separate module, so
81// `const` emits a second copy of all four files — 11.8 MB of duplicated font in
82// the wasm.
83/// Excalifont ("Sketchy"), embedded at compile time.
84pub static EXCALIFONT: &[u8] = include_bytes!("fonts/Excalifont-Regular.ttf");
85/// Roboto ("Basic") — Google's Roboto, embedded at compile time.
86pub static ROBOTO: &[u8] = include_bytes!("fonts/Roboto-Regular.ttf");
87/// Forum ("Formal") — Google's Forum, embedded at compile time.
88pub static FORUM: &[u8] = include_bytes!("fonts/Forum-Regular.ttf");
89/// Iosevka ("Monospace"), embedded at compile time — SUBSET, not the full
90/// face. The full build is 10.8 MB of mostly full-Unicode glyphs and twenty
91/// stylistic sets the shaper never enables; this 222 KB subset keeps Latin,
92/// Greek (for Ω/µ-grade labels), punctuation, sub/superscripts, currency,
93/// letterlike, arrows, math operators, box drawing, blocks and geometric
94/// shapes, with hinting and the default layout features. A label using a
95/// character outside those ranges falls back per the host's family list. To
96/// regenerate from a full Iosevka-Regular.ttf:
97///
98/// ```text
99/// python3 -m fontTools.subset Iosevka-Regular.ttf \
100///   --unicodes="U+0000-024F,U+0370-03FF,U+2000-206F,U+2070-209F,\
101/// U+20A0-20CF,U+2100-214F,U+2190-21FF,U+2200-22FF,U+2500-257F,\
102/// U+2580-259F,U+25A0-25FF" \
103///   --no-glyph-names --output-file=src/fonts/Iosevka-Regular.ttf
104/// ```
105pub static IOSEVKA: &[u8] = include_bytes!("fonts/Iosevka-Regular.ttf");