1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/// One font file of a file-backed family, with the weight and style the file provides.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FontFile {
/// Path of the font file.
pub path: String,
/// Weight of the face in the file.
pub weight: FontWeight,
/// Upright or italic style of the face in the file.
pub style: FontStyle,
}
impl FontFile {
/// Creates an entry for the font file at `path` with normal weight and upright style.
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
weight: FontWeight::NORMAL,
style: FontStyle::Normal,
}
}
/// Sets the weight the file provides.
pub fn with_weight(mut self, weight: FontWeight) -> Self {
self.weight = weight;
self
}
/// Sets the style the file provides.
pub fn with_style(mut self, style: FontStyle) -> Self {
self.style = style;
self
}
}
/// A font family made of font files, one for each weight and style.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FileBackedFontFamily {
/// The files of the family. Never empty when built with [`FileBackedFontFamily::new`].
pub fonts: Vec<FontFile>,
}
/// Error returned when a font family cannot be built.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FontFamilyError {
/// The list of font files was empty.
EmptyFileList,
}
impl std::fmt::Display for FontFamilyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EmptyFileList => {
write!(f, "file-backed font family requires at least one font file")
}
}
}
}
impl std::error::Error for FontFamilyError {}
impl FileBackedFontFamily {
/// Builds a family from `fonts`, or returns [`FontFamilyError::EmptyFileList`] when the list
/// is empty.
pub fn new(fonts: Vec<FontFile>) -> Result<Self, FontFamilyError> {
if fonts.is_empty() {
return Err(FontFamilyError::EmptyFileList);
}
Ok(Self { fonts })
}
}
/// A font family identified by the path of one typeface file.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct LoadedTypefacePath {
/// Path of the typeface file.
pub path: String,
}
impl LoadedTypefacePath {
/// Creates a family for the typeface file at `path`.
pub fn new(path: impl Into<String>) -> Self {
Self { path: path.into() }
}
}
/// The font family of a text. Compose's `FontFamily`.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum FontFamily {
/// The default font of the platform.
#[default]
Default,
/// A generic sans-serif font.
SansSerif,
/// A generic serif font.
Serif,
/// A generic monospace font.
Monospace,
/// A generic cursive font.
Cursive,
/// A generic fantasy font.
Fantasy,
/// A family looked up by name among the fonts the renderer has.
Named(String),
/// A family built from font files.
FileBacked(FileBackedFontFamily),
/// A family identified by the path of a typeface file.
LoadedTypeface(LoadedTypefacePath),
}
impl FontFamily {
/// Returns the family for `name`, as [`FontFamily::from_name`] does.
pub fn named(name: impl Into<String>) -> Self {
Self::from_name(&name.into())
}
/// Returns the generic family for `name` when it is a generic name such as `serif` or
/// `sans-serif`, or an empty or default name, and `Named` for any other name.
pub fn from_name(name: &str) -> Self {
match name {
"" | "Default" | "default" => Self::Default,
"SansSerif" | "sans-serif" => Self::SansSerif,
"Serif" | "serif" => Self::Serif,
"Monospace" | "monospace" => Self::Monospace,
"Cursive" | "cursive" => Self::Cursive,
"Fantasy" | "fantasy" => Self::Fantasy,
value => Self::Named(value.to_string()),
}
}
/// Builds a `FileBacked` family from `fonts`, or fails when the list is empty.
pub fn file_backed(fonts: Vec<FontFile>) -> Result<Self, FontFamilyError> {
FileBackedFontFamily::new(fonts).map(Self::FileBacked)
}
/// Builds a `LoadedTypeface` family for the typeface file at `path`.
pub fn loaded_typeface_path(path: impl Into<String>) -> Self {
Self::LoadedTypeface(LoadedTypefacePath::new(path))
}
/// Returns the name of a `Named` family, and `None` for every other variant.
pub fn family_name(&self) -> Option<&str> {
match self {
Self::Named(name) => Some(name.as_str()),
_ => None,
}
}
}
impl From<&str> for FontFamily {
fn from(value: &str) -> Self {
Self::from_name(value)
}
}
impl From<String> for FontFamily {
fn from(value: String) -> Self {
Self::from_name(value.as_str())
}
}
/// Weight of a font, from 1 (thinnest) to 1000 (heaviest), as in Compose's `FontWeight`.
/// 400 is normal and 700 is bold.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FontWeight(pub u16);
impl FontWeight {
/// Creates a weight, clamping `weight` into the range 1 to 1000.
pub const fn new(weight: u16) -> Self {
Self::clamped(weight)
}
/// Creates a weight, or returns `None` when `weight` is outside the range 1 to 1000.
pub const fn try_new(weight: u16) -> Option<Self> {
if weight >= 1 && weight <= 1000 {
Some(Self(weight))
} else {
None
}
}
/// Creates a weight, clamping `weight` into the range 1 to 1000.
pub const fn clamped(weight: u16) -> Self {
if weight < 1 {
Self(1)
} else if weight > 1000 {
Self(1000)
} else {
Self(weight)
}
}
/// Returns the weight as a number from 1 to 1000.
pub const fn value(self) -> u16 {
self.0
}
/// Weight 100.
pub const THIN: Self = Self(100);
/// Weight 200.
pub const EXTRA_LIGHT: Self = Self(200);
/// Weight 300.
pub const LIGHT: Self = Self(300);
/// Weight 400.
pub const NORMAL: Self = Self(400);
/// Weight 500.
pub const MEDIUM: Self = Self(500);
/// Weight 600.
pub const SEMI_BOLD: Self = Self(600);
/// Weight 700.
pub const BOLD: Self = Self(700);
/// Weight 800.
pub const EXTRA_BOLD: Self = Self(800);
/// Weight 900.
pub const BLACK: Self = Self(900);
/// Weight 100, the same as [`FontWeight::THIN`].
pub const W100: Self = Self(100);
/// Weight 200, the same as [`FontWeight::EXTRA_LIGHT`].
pub const W200: Self = Self(200);
/// Weight 300, the same as [`FontWeight::LIGHT`].
pub const W300: Self = Self(300);
/// Weight 400, the same as [`FontWeight::NORMAL`].
pub const W400: Self = Self(400);
/// Weight 500, the same as [`FontWeight::MEDIUM`].
pub const W500: Self = Self(500);
/// Weight 600, the same as [`FontWeight::SEMI_BOLD`].
pub const W600: Self = Self(600);
/// Weight 700, the same as [`FontWeight::BOLD`].
pub const W700: Self = Self(700);
/// Weight 800, the same as [`FontWeight::EXTRA_BOLD`].
pub const W800: Self = Self(800);
/// Weight 900, the same as [`FontWeight::BLACK`].
pub const W900: Self = Self(900);
}
impl Default for FontWeight {
fn default() -> Self {
Self::NORMAL
}
}
/// Whether glyphs are upright or italic. Compose's `FontStyle`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum FontStyle {
/// Upright glyphs.
#[default]
Normal,
/// Italic glyphs.
Italic,
}
/// Which missing styles the renderer may fake when the chosen font lacks them.
/// Compose's `FontSynthesis`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum FontSynthesis {
/// Nothing is synthesized.
#[default]
None,
/// Both bold and italic may be synthesized.
All,
/// Only bold may be synthesized.
Weight,
/// Only italic may be synthesized.
Style,
}
#[cfg(test)]
#[path = "tests/font_tests.rs"]
mod tests;