1#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3pub struct FontFile {
4 pub path: String,
6 pub weight: FontWeight,
8 pub style: FontStyle,
10}
11
12impl FontFile {
13 pub fn new(path: impl Into<String>) -> Self {
15 Self {
16 path: path.into(),
17 weight: FontWeight::NORMAL,
18 style: FontStyle::Normal,
19 }
20 }
21
22 pub fn with_weight(mut self, weight: FontWeight) -> Self {
24 self.weight = weight;
25 self
26 }
27
28 pub fn with_style(mut self, style: FontStyle) -> Self {
30 self.style = style;
31 self
32 }
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Hash)]
37pub struct FileBackedFontFamily {
38 pub fonts: Vec<FontFile>,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum FontFamilyError {
45 EmptyFileList,
47}
48
49impl std::fmt::Display for FontFamilyError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 FontFamilyError::EmptyFileList => {
53 write!(f, "file-backed font family requires at least one font file")
54 }
55 }
56 }
57}
58
59impl std::error::Error for FontFamilyError {}
60
61impl FileBackedFontFamily {
62 pub fn new(fonts: Vec<FontFile>) -> Result<Self, FontFamilyError> {
65 if fonts.is_empty() {
66 return Err(FontFamilyError::EmptyFileList);
67 }
68 Ok(Self { fonts })
69 }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, Hash)]
74pub struct LoadedTypefacePath {
75 pub path: String,
77}
78
79impl LoadedTypefacePath {
80 pub fn new(path: impl Into<String>) -> Self {
82 Self { path: path.into() }
83 }
84}
85
86#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
88pub enum FontFamily {
89 #[default]
91 Default,
92 SansSerif,
94 Serif,
96 Monospace,
98 Cursive,
100 Fantasy,
102 Named(String),
104 FileBacked(FileBackedFontFamily),
106 LoadedTypeface(LoadedTypefacePath),
108}
109
110impl FontFamily {
111 pub fn named(name: impl Into<String>) -> Self {
113 Self::from_name(&name.into())
114 }
115
116 pub fn from_name(name: &str) -> Self {
119 match name {
120 "" | "Default" | "default" => Self::Default,
121 "SansSerif" | "sans-serif" => Self::SansSerif,
122 "Serif" | "serif" => Self::Serif,
123 "Monospace" | "monospace" => Self::Monospace,
124 "Cursive" | "cursive" => Self::Cursive,
125 "Fantasy" | "fantasy" => Self::Fantasy,
126 value => Self::Named(value.to_string()),
127 }
128 }
129
130 pub fn file_backed(fonts: Vec<FontFile>) -> Result<Self, FontFamilyError> {
132 FileBackedFontFamily::new(fonts).map(Self::FileBacked)
133 }
134
135 pub fn loaded_typeface_path(path: impl Into<String>) -> Self {
137 Self::LoadedTypeface(LoadedTypefacePath::new(path))
138 }
139
140 pub fn family_name(&self) -> Option<&str> {
142 match self {
143 Self::Named(name) => Some(name.as_str()),
144 _ => None,
145 }
146 }
147}
148
149impl From<&str> for FontFamily {
150 fn from(value: &str) -> Self {
151 Self::from_name(value)
152 }
153}
154
155impl From<String> for FontFamily {
156 fn from(value: String) -> Self {
157 Self::from_name(value.as_str())
158 }
159}
160
161#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
164pub struct FontWeight(pub u16);
165
166impl FontWeight {
167 pub const fn new(weight: u16) -> Self {
169 Self::clamped(weight)
170 }
171
172 pub const fn try_new(weight: u16) -> Option<Self> {
174 if weight >= 1 && weight <= 1000 {
175 Some(Self(weight))
176 } else {
177 None
178 }
179 }
180
181 pub const fn clamped(weight: u16) -> Self {
183 if weight < 1 {
184 Self(1)
185 } else if weight > 1000 {
186 Self(1000)
187 } else {
188 Self(weight)
189 }
190 }
191
192 pub const fn value(self) -> u16 {
194 self.0
195 }
196
197 pub const THIN: Self = Self(100);
199 pub const EXTRA_LIGHT: Self = Self(200);
201 pub const LIGHT: Self = Self(300);
203 pub const NORMAL: Self = Self(400);
205 pub const MEDIUM: Self = Self(500);
207 pub const SEMI_BOLD: Self = Self(600);
209 pub const BOLD: Self = Self(700);
211 pub const EXTRA_BOLD: Self = Self(800);
213 pub const BLACK: Self = Self(900);
215 pub const W100: Self = Self(100);
217 pub const W200: Self = Self(200);
219 pub const W300: Self = Self(300);
221 pub const W400: Self = Self(400);
223 pub const W500: Self = Self(500);
225 pub const W600: Self = Self(600);
227 pub const W700: Self = Self(700);
229 pub const W800: Self = Self(800);
231 pub const W900: Self = Self(900);
233}
234
235impl Default for FontWeight {
236 fn default() -> Self {
237 Self::NORMAL
238 }
239}
240
241#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
243pub enum FontStyle {
244 #[default]
246 Normal,
247 Italic,
249}
250
251#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
254pub enum FontSynthesis {
255 #[default]
257 None,
258 All,
260 Weight,
262 Style,
264}
265
266#[cfg(test)]
267#[path = "tests/font_tests.rs"]
268mod tests;