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