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}