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");