1use crate::{
2 color::Color,
3 json::{json, ToJson},
4};
5pub use font_kit::{
6 metrics::Metrics,
7 properties::{Properties, Stretch, Style, Weight},
8};
9use serde::{de, Deserialize};
10use serde_json::Value;
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
13pub struct FontId(pub usize);
14
15pub type GlyphId = u32;
16
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
18pub struct TextStyle {
19 pub color: Color,
20 pub font_properties: Properties,
21}
22
23#[allow(non_camel_case_types)]
24#[derive(Deserialize)]
25enum WeightJson {
26 thin,
27 extra_light,
28 light,
29 normal,
30 medium,
31 semibold,
32 bold,
33 extra_bold,
34 black,
35}
36
37#[derive(Deserialize)]
38struct TextStyleJson {
39 color: Color,
40 weight: Option<WeightJson>,
41 #[serde(default)]
42 italic: bool,
43}
44
45impl<'de> Deserialize<'de> for TextStyle {
46 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47 where
48 D: serde::Deserializer<'de>,
49 {
50 let json = Value::deserialize(deserializer)?;
51 if json.is_object() {
52 let style_json: TextStyleJson =
53 serde_json::from_value(json).map_err(de::Error::custom)?;
54 Ok(style_json.into())
55 } else {
56 Ok(Self {
57 color: serde_json::from_value(json).map_err(de::Error::custom)?,
58 font_properties: Properties::new(),
59 })
60 }
61 }
62}
63
64impl From<Color> for TextStyle {
65 fn from(color: Color) -> Self {
66 Self {
67 color,
68 font_properties: Default::default(),
69 }
70 }
71}
72
73impl ToJson for TextStyle {
74 fn to_json(&self) -> Value {
75 json!({
76 "color": self.color.to_json(),
77 "font_properties": self.font_properties.to_json(),
78 })
79 }
80}
81
82impl Into<TextStyle> for TextStyleJson {
83 fn into(self) -> TextStyle {
84 let weight = match self.weight.unwrap_or(WeightJson::normal) {
85 WeightJson::thin => Weight::THIN,
86 WeightJson::extra_light => Weight::EXTRA_LIGHT,
87 WeightJson::light => Weight::LIGHT,
88 WeightJson::normal => Weight::NORMAL,
89 WeightJson::medium => Weight::MEDIUM,
90 WeightJson::semibold => Weight::SEMIBOLD,
91 WeightJson::bold => Weight::BOLD,
92 WeightJson::extra_bold => Weight::EXTRA_BOLD,
93 WeightJson::black => Weight::BLACK,
94 };
95 let style = if self.italic {
96 Style::Italic
97 } else {
98 Style::Normal
99 };
100 TextStyle {
101 color: self.color,
102 font_properties: *Properties::new().weight(weight).style(style),
103 }
104 }
105}
106
107impl ToJson for Properties {
108 fn to_json(&self) -> crate::json::Value {
109 json!({
110 "style": self.style.to_json(),
111 "weight": self.weight.to_json(),
112 "stretch": self.stretch.to_json(),
113 })
114 }
115}
116
117impl ToJson for Style {
118 fn to_json(&self) -> crate::json::Value {
119 match self {
120 Style::Normal => json!("normal"),
121 Style::Italic => json!("italic"),
122 Style::Oblique => json!("oblique"),
123 }
124 }
125}
126
127impl ToJson for Weight {
128 fn to_json(&self) -> crate::json::Value {
129 if self.0 == Weight::THIN.0 {
130 json!("thin")
131 } else if self.0 == Weight::EXTRA_LIGHT.0 {
132 json!("extra light")
133 } else if self.0 == Weight::LIGHT.0 {
134 json!("light")
135 } else if self.0 == Weight::NORMAL.0 {
136 json!("normal")
137 } else if self.0 == Weight::MEDIUM.0 {
138 json!("medium")
139 } else if self.0 == Weight::SEMIBOLD.0 {
140 json!("semibold")
141 } else if self.0 == Weight::BOLD.0 {
142 json!("bold")
143 } else if self.0 == Weight::EXTRA_BOLD.0 {
144 json!("extra bold")
145 } else if self.0 == Weight::BLACK.0 {
146 json!("black")
147 } else {
148 json!(self.0)
149 }
150 }
151}
152
153impl ToJson for Stretch {
154 fn to_json(&self) -> serde_json::Value {
155 json!(self.0)
156 }
157}