livesplit_core/settings/
font.rs

1use crate::platform::prelude::*;
2use serde::{Deserialize, Serialize};
3
4/// Describes a Font to visualize text with. Depending on the platform, a font
5/// that matches the settings most closely is chosen. The settings may be
6/// ignored entirely if the platform can't support different fonts, such as in a
7/// terminal.
8#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
9pub struct Font {
10    /// The family name of the font to use. This corresponds with the
11    /// `Typographic Family Name` (Name ID 16) in the name table of the font. If
12    /// no such entry exists, the `Font Family Name` (Name ID 1) is to be used
13    /// instead. If there are multiple entries for the name, the english entry
14    /// is the one to choose. The subfamily is not specified at all, and instead
15    /// a suitable subfamily is chosen based on the style, weight and stretch
16    /// values.
17    ///
18    /// [`name — Naming Table` on Microsoft
19    /// Docs](https://docs.microsoft.com/en-us/typography/opentype/spec/name)
20    ///
21    /// This is to ensure the highest portability across various platforms.
22    /// Platforms often select fonts very differently, so if necessary it is
23    /// also fine to store a different font identifier here at the cost of
24    /// sacrificing portability.
25    pub family: String,
26    /// The style of the font to prefer selecting.
27    pub style: Style,
28    /// The weight of the font to prefer selecting.
29    pub weight: Weight,
30    /// The stretch of the font to prefer selecting.
31    pub stretch: Stretch,
32}
33
34/// The style specifies whether to use a normal or italic version of a font. The
35/// style may be emulated if no font dedicated to the style can be found.
36#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
37#[serde(rename_all = "lowercase")]
38pub enum Style {
39    /// Select a regular, non-italic version of the font.
40    #[default]
41    Normal,
42    /// Select an italic version of the font.
43    Italic,
44}
45
46impl Style {
47    /// The value to assign to the `ital` variation axis.
48    pub const fn value_for_italic(self) -> f32 {
49        match self {
50            Style::Normal => 0.0,
51            Style::Italic => 1.0,
52        }
53    }
54}
55
56/// The weight specifies the weight / boldness of a font. If there is no font
57/// with the exact weight value, a font with a similar weight is to be chosen
58/// based on an algorithm similar to this:
59///
60/// [`Fallback weights` on
61/// MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#Fallback_weights)
62#[derive(
63    Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
64)]
65#[serde(rename_all = "kebab-case")]
66pub enum Weight {
67    /// 100 (also known as Hairline)
68    Thin,
69    /// 200 (also known as Ultra Light)
70    ExtraLight,
71    /// 300
72    Light,
73    /// 350 (also known as Demi Light)
74    SemiLight,
75    /// 400 (also known as Regular)
76    #[default]
77    Normal,
78    /// 500
79    Medium,
80    /// 600 (also known as Demi Bold)
81    SemiBold,
82    /// 700
83    Bold,
84    /// 800 (also known as Ultra Bold)
85    ExtraBold,
86    /// 900 (also known as Heavy)
87    Black,
88    /// 950 (also known as Ultra Black)
89    ExtraBlack,
90}
91
92impl Weight {
93    /// The numeric value of the weight.
94    pub const fn value(self) -> f32 {
95        match self {
96            Weight::Thin => 100.0,
97            Weight::ExtraLight => 200.0,
98            Weight::Light => 300.0,
99            Weight::SemiLight => 350.0,
100            Weight::Normal => 400.0,
101            Weight::Medium => 500.0,
102            Weight::SemiBold => 600.0,
103            Weight::Bold => 700.0,
104            Weight::ExtraBold => 800.0,
105            Weight::Black => 900.0,
106            Weight::ExtraBlack => 950.0,
107        }
108    }
109}
110
111/// The stretch specifies how wide a font should be. For example, it may make
112/// sense to reduce the stretch of a font to ensure split names are not cut off.
113/// A font with a stretch value that is close is to be selected.
114///
115/// [`Font face selection` on
116/// MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch#Font_face_selection)
117#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
118#[serde(rename_all = "kebab-case")]
119pub enum Stretch {
120    /// 50%
121    UltraCondensed,
122    /// 62.5%
123    ExtraCondensed,
124    /// 75%
125    Condensed,
126    /// 87.5%
127    SemiCondensed,
128    /// 100%
129    #[default]
130    Normal,
131    /// 112.5%
132    SemiExpanded,
133    /// 125%
134    Expanded,
135    /// 150%
136    ExtraExpanded,
137    /// 200%
138    UltraExpanded,
139}
140
141impl Stretch {
142    /// The percentage the font is stretched by (50% to 200%).
143    pub const fn percentage(self) -> f32 {
144        match self {
145            Stretch::UltraCondensed => 50.0,
146            Stretch::ExtraCondensed => 62.5,
147            Stretch::Condensed => 75.0,
148            Stretch::SemiCondensed => 87.5,
149            Stretch::Normal => 100.0,
150            Stretch::SemiExpanded => 112.5,
151            Stretch::Expanded => 125.0,
152            Stretch::ExtraExpanded => 150.0,
153            Stretch::UltraExpanded => 200.0,
154        }
155    }
156
157    /// The factor the font is stretched by (0x to 2x).
158    pub const fn factor(self) -> f32 {
159        match self {
160            Stretch::UltraCondensed => 0.5,
161            Stretch::ExtraCondensed => 0.625,
162            Stretch::Condensed => 0.75,
163            Stretch::SemiCondensed => 0.875,
164            Stretch::Normal => 1.0,
165            Stretch::SemiExpanded => 1.125,
166            Stretch::Expanded => 1.25,
167            Stretch::ExtraExpanded => 1.5,
168            Stretch::UltraExpanded => 2.0,
169        }
170    }
171}