summaryrefslogtreecommitdiff
path: root/src/font/variant.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-09-19 17:44:40 +0200
committerLaurenz <laurmaedje@gmail.com>2022-09-19 17:44:40 +0200
commite29f55bb294cc298daad97accf6d8a76976b409c (patch)
treecc4db3b61fa23e13f781e992c63427d36e77ef8c /src/font/variant.rs
parent59f67b79c7ff50f0bc9a27373d0fa36d1523e08a (diff)
Remove font store
Diffstat (limited to 'src/font/variant.rs')
-rw-r--r--src/font/variant.rs226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/font/variant.rs b/src/font/variant.rs
new file mode 100644
index 00000000..9e16afc8
--- /dev/null
+++ b/src/font/variant.rs
@@ -0,0 +1,226 @@
+use std::fmt::{self, Debug, Formatter};
+
+use serde::{Deserialize, Serialize};
+
+/// Properties that distinguish a font from other fonts in the same family.
+#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
+pub struct FontVariant {
+ /// The style of the font (normal / italic / oblique).
+ pub style: FontStyle,
+ /// How heavy the font is (100 - 900).
+ pub weight: FontWeight,
+ /// How condensed or expanded the font is (0.5 - 2.0).
+ pub stretch: FontStretch,
+}
+
+impl FontVariant {
+ /// Create a variant from its three components.
+ pub fn new(style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Self {
+ Self { style, weight, stretch }
+ }
+}
+
+impl Debug for FontVariant {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{:?}-{:?}-{:?}", self.style, self.weight, self.stretch)
+ }
+}
+
+/// The style of a font.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum FontStyle {
+ /// The default style.
+ Normal,
+ /// A cursive style.
+ Italic,
+ /// A slanted style.
+ Oblique,
+}
+
+impl FontStyle {
+ /// The conceptual distance between the styles, expressed as a number.
+ pub fn distance(self, other: Self) -> u16 {
+ if self == other {
+ 0
+ } else if self != Self::Normal && other != Self::Normal {
+ 1
+ } else {
+ 2
+ }
+ }
+}
+
+impl Default for FontStyle {
+ fn default() -> Self {
+ Self::Normal
+ }
+}
+
+/// The weight of a font.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct FontWeight(u16);
+
+impl FontWeight {
+ /// Thin weight (100).
+ pub const THIN: Self = Self(100);
+
+ /// Extra light weight (200).
+ pub const EXTRALIGHT: Self = Self(200);
+
+ /// Light weight (300).
+ pub const LIGHT: Self = Self(300);
+
+ /// Regular weight (400).
+ pub const REGULAR: Self = Self(400);
+
+ /// Medium weight (500).
+ pub const MEDIUM: Self = Self(500);
+
+ /// Semibold weight (600).
+ pub const SEMIBOLD: Self = Self(600);
+
+ /// Bold weight (700).
+ pub const BOLD: Self = Self(700);
+
+ /// Extrabold weight (800).
+ pub const EXTRABOLD: Self = Self(800);
+
+ /// Black weight (900).
+ pub const BLACK: Self = Self(900);
+
+ /// Create a font weight from a number between 100 and 900, clamping it if
+ /// necessary.
+ pub fn from_number(weight: u16) -> Self {
+ Self(weight.max(100).min(900))
+ }
+
+ /// The number between 100 and 900.
+ pub fn to_number(self) -> u16 {
+ self.0
+ }
+
+ /// Add (or remove) weight, saturating at the boundaries of 100 and 900.
+ pub fn thicken(self, delta: i16) -> Self {
+ Self((self.0 as i16).saturating_add(delta).max(100).min(900) as u16)
+ }
+
+ /// The absolute number distance between this and another font weight.
+ pub fn distance(self, other: Self) -> u16 {
+ (self.0 as i16 - other.0 as i16).abs() as u16
+ }
+}
+
+impl Default for FontWeight {
+ fn default() -> Self {
+ Self::REGULAR
+ }
+}
+
+impl Debug for FontWeight {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+/// The width of a font.
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct FontStretch(u16);
+
+impl FontStretch {
+ /// Ultra-condensed stretch (50%).
+ pub const ULTRA_CONDENSED: Self = Self(500);
+
+ /// Extra-condensed stretch weight (62.5%).
+ pub const EXTRA_CONDENSED: Self = Self(625);
+
+ /// Condensed stretch (75%).
+ pub const CONDENSED: Self = Self(750);
+
+ /// Semi-condensed stretch (87.5%).
+ pub const SEMI_CONDENSED: Self = Self(875);
+
+ /// Normal stretch (100%).
+ pub const NORMAL: Self = Self(1000);
+
+ /// Semi-expanded stretch (112.5%).
+ pub const SEMI_EXPANDED: Self = Self(1125);
+
+ /// Expanded stretch (125%).
+ pub const EXPANDED: Self = Self(1250);
+
+ /// Extra-expanded stretch (150%).
+ pub const EXTRA_EXPANDED: Self = Self(1500);
+
+ /// Ultra-expanded stretch (200%).
+ pub const ULTRA_EXPANDED: Self = Self(2000);
+
+ /// Create a font stretch from a ratio between 0.5 and 2.0, clamping it if
+ /// necessary.
+ pub fn from_ratio(ratio: f32) -> Self {
+ Self((ratio.max(0.5).min(2.0) * 1000.0) as u16)
+ }
+
+ /// Create a font stretch from an OpenType-style number between 1 and 9,
+ /// clamping it if necessary.
+ pub fn from_number(stretch: u16) -> Self {
+ match stretch {
+ 0 | 1 => Self::ULTRA_CONDENSED,
+ 2 => Self::EXTRA_CONDENSED,
+ 3 => Self::CONDENSED,
+ 4 => Self::SEMI_CONDENSED,
+ 5 => Self::NORMAL,
+ 6 => Self::SEMI_EXPANDED,
+ 7 => Self::EXPANDED,
+ 8 => Self::EXTRA_EXPANDED,
+ _ => Self::ULTRA_EXPANDED,
+ }
+ }
+
+ /// The ratio between 0.5 and 2.0 corresponding to this stretch.
+ pub fn to_ratio(self) -> f32 {
+ self.0 as f32 / 1000.0
+ }
+
+ /// The absolute ratio distance between this and another font stretch.
+ pub fn distance(self, other: Self) -> f32 {
+ (self.to_ratio() - other.to_ratio()).abs()
+ }
+}
+
+impl Default for FontStretch {
+ fn default() -> Self {
+ Self::NORMAL
+ }
+}
+
+impl Debug for FontStretch {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}%", 100.0 * self.to_ratio())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_font_weight_distance() {
+ let d = |a, b| FontWeight(a).distance(FontWeight(b));
+ assert_eq!(d(500, 200), 300);
+ assert_eq!(d(500, 500), 0);
+ assert_eq!(d(500, 900), 400);
+ assert_eq!(d(10, 100), 90);
+ }
+
+ #[test]
+ fn test_font_stretch_debug() {
+ assert_eq!(format!("{:?}", FontStretch::EXPANDED), "125%")
+ }
+}