1 use super::color::Color;
2 use super::font::{FontDesc, FontError, FontFamily, FontStyle, FontTransform};
3 use super::size::{HasDimension, SizeDesc};
4 use super::BLACK;
5 pub use plotters_backend::text_anchor;
6 use plotters_backend::{BackendColor, BackendCoord, BackendStyle, BackendTextStyle};
7 
8 /// Style of a text
9 #[derive(Clone)]
10 pub struct TextStyle<'a> {
11     /// The font description
12     pub font: FontDesc<'a>,
13     /// The text color
14     pub color: BackendColor,
15     /// The anchor point position
16     pub pos: text_anchor::Pos,
17 }
18 
19 /// Trait for values that can be converted into `TextStyle` values
20 pub trait IntoTextStyle<'a> {
21     /** Converts the value into a TextStyle value.
22 
23     `parent` is used in some cases to convert a font size from points to pixels.
24 
25     # Example
26 
27     ```
28     use plotters::prelude::*;
29     let drawing_area = SVGBackend::new("into_text_style.svg", (200, 100)).into_drawing_area();
30     drawing_area.fill(&WHITE).unwrap();
31     let text_style = ("sans-serif", 20, &RED).into_text_style(&drawing_area);
32     drawing_area.draw_text("This is a big red label", &text_style, (10, 50)).unwrap();
33     ```
34 
35     The result is a text label styled accordingly:
36 
37     ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@f030ed3/apidoc/into_text_style.svg)
38 
39     */
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>40     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>;
41 
42     /** Specifies the color of the text element
43 
44     # Example
45 
46     ```
47     use plotters::prelude::*;
48     let drawing_area = SVGBackend::new("with_color.svg", (200, 100)).into_drawing_area();
49     drawing_area.fill(&WHITE).unwrap();
50     let text_style = ("sans-serif", 20).with_color(RED).into_text_style(&drawing_area);
51     drawing_area.draw_text("This is a big red label", &text_style, (10, 50)).unwrap();
52     ```
53 
54     The result is a text label styled accordingly:
55 
56     ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@f030ed3/apidoc/with_color.svg)
57 
58     # See also
59 
60     [`FontDesc::color()`]
61 
62     [`IntoTextStyle::into_text_style()`] for a more succinct example
63 
64     */
with_color<C: Color>(self, color: C) -> TextStyleBuilder<'a, Self> where Self: Sized,65     fn with_color<C: Color>(self, color: C) -> TextStyleBuilder<'a, Self>
66     where
67         Self: Sized,
68     {
69         TextStyleBuilder {
70             base: self,
71             new_color: Some(color.to_backend_color()),
72             new_pos: None,
73             _phatom: std::marker::PhantomData,
74         }
75     }
76 
77     /** Specifies the position of the text anchor relative to the text element
78 
79     # Example
80 
81     ```
82     use plotters::{prelude::*,style::text_anchor::{HPos, Pos, VPos}};
83     let anchor_position = (200,100);
84     let anchor_left_bottom = Pos::new(HPos::Left, VPos::Bottom);
85     let anchor_right_top = Pos::new(HPos::Right, VPos::Top);
86     let drawing_area = SVGBackend::new("with_anchor.svg", (400, 200)).into_drawing_area();
87     drawing_area.fill(&WHITE).unwrap();
88     drawing_area.draw(&Circle::new(anchor_position, 5, RED.filled()));
89     let text_style_right_top = BLACK.with_anchor::<RGBColor>(anchor_right_top).into_text_style(&drawing_area);
90     drawing_area.draw_text("The anchor sits at the right top of this label", &text_style_right_top, anchor_position);
91     let text_style_left_bottom = BLACK.with_anchor::<RGBColor>(anchor_left_bottom).into_text_style(&drawing_area);
92     drawing_area.draw_text("The anchor sits at the left bottom of this label", &text_style_left_bottom, anchor_position);
93     ```
94 
95     The result has a red pixel at the center and two text labels positioned accordingly:
96 
97     ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@b0b94d5/apidoc/with_anchor.svg)
98 
99     # See also
100 
101     [`TextStyle::pos()`]
102 
103     */
with_anchor<C: Color>(self, pos: text_anchor::Pos) -> TextStyleBuilder<'a, Self> where Self: Sized,104     fn with_anchor<C: Color>(self, pos: text_anchor::Pos) -> TextStyleBuilder<'a, Self>
105     where
106         Self: Sized,
107     {
108         TextStyleBuilder {
109             base: self,
110             new_pos: Some(pos),
111             new_color: None,
112             _phatom: std::marker::PhantomData,
113         }
114     }
115 }
116 
117 pub struct TextStyleBuilder<'a, T: IntoTextStyle<'a>> {
118     base: T,
119     new_color: Option<BackendColor>,
120     new_pos: Option<text_anchor::Pos>,
121     _phatom: std::marker::PhantomData<&'a T>,
122 }
123 
124 impl<'a, T: IntoTextStyle<'a>> IntoTextStyle<'a> for TextStyleBuilder<'a, T> {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>125     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
126         let mut base = self.base.into_text_style(parent);
127         if let Some(color) = self.new_color {
128             base.color = color;
129         }
130         if let Some(pos) = self.new_pos {
131             base = base.pos(pos);
132         }
133         base
134     }
135 }
136 
137 impl<'a> TextStyle<'a> {
138     /// Sets the color of the style.
139     ///
140     /// - `color`: The required color
141     /// - **returns** The up-to-dated text style
142     ///
143     /// ```rust
144     /// use plotters::prelude::*;
145     ///
146     /// let style = TextStyle::from(("sans-serif", 20).into_font()).color(&RED);
147     /// ```
color<C: Color>(&self, color: &'a C) -> Self148     pub fn color<C: Color>(&self, color: &'a C) -> Self {
149         Self {
150             font: self.font.clone(),
151             color: color.to_backend_color(),
152             pos: self.pos,
153         }
154     }
155 
156     /// Sets the font transformation of the style.
157     ///
158     /// - `trans`: The required transformation
159     /// - **returns** The up-to-dated text style
160     ///
161     /// ```rust
162     /// use plotters::prelude::*;
163     ///
164     /// let style = TextStyle::from(("sans-serif", 20).into_font()).transform(FontTransform::Rotate90);
165     /// ```
transform(&self, trans: FontTransform) -> Self166     pub fn transform(&self, trans: FontTransform) -> Self {
167         Self {
168             font: self.font.clone().transform(trans),
169             color: self.color,
170             pos: self.pos,
171         }
172     }
173 
174     /// Sets the anchor position.
175     ///
176     /// - `pos`: The required anchor position
177     /// - **returns** The up-to-dated text style
178     ///
179     /// ```rust
180     /// use plotters::prelude::*;
181     /// use plotters::style::text_anchor::{Pos, HPos, VPos};
182     ///
183     /// let pos = Pos::new(HPos::Left, VPos::Top);
184     /// let style = TextStyle::from(("sans-serif", 20).into_font()).pos(pos);
185     /// ```
186     ///
187     /// # See also
188     ///
189     /// [`IntoTextStyle::with_anchor()`]
pos(&self, pos: text_anchor::Pos) -> Self190     pub fn pos(&self, pos: text_anchor::Pos) -> Self {
191         Self {
192             font: self.font.clone(),
193             color: self.color,
194             pos,
195         }
196     }
197 }
198 
199 impl<'a> IntoTextStyle<'a> for FontDesc<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>200     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
201         self.into()
202     }
203 }
204 
205 impl<'a> IntoTextStyle<'a> for TextStyle<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>206     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
207         self
208     }
209 }
210 
211 impl<'a> IntoTextStyle<'a> for &'a str {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>212     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
213         self.into()
214     }
215 }
216 
217 impl<'a> IntoTextStyle<'a> for FontFamily<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>218     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
219         self.into()
220     }
221 }
222 
223 impl IntoTextStyle<'static> for u32 {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static>224     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
225         TextStyle::from((FontFamily::SansSerif, self))
226     }
227 }
228 
229 impl IntoTextStyle<'static> for f64 {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static>230     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
231         TextStyle::from((FontFamily::SansSerif, self))
232     }
233 }
234 
235 impl<'a, T: Color> IntoTextStyle<'a> for &'a T {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>236     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
237         TextStyle::from(FontFamily::SansSerif).color(self)
238     }
239 }
240 
241 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>242     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
243         (self.0.into(), self.1.in_pixels(parent)).into()
244     }
245 }
246 
247 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a> for (F, T, &'a C) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>248     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
249         IntoTextStyle::into_text_style((self.0, self.1), parent).color(self.2)
250     }
251 }
252 
253 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T, FontStyle) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>254     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
255         (self.0.into(), self.1.in_pixels(parent), self.2).into()
256     }
257 }
258 
259 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a>
260     for (F, T, FontStyle, &'a C)
261 {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>262     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
263         IntoTextStyle::into_text_style((self.0, self.1, self.2), parent).color(self.3)
264     }
265 }
266 
267 /// Make sure that we are able to automatically copy the `TextStyle`
268 impl<'a, 'b: 'a> From<&'b TextStyle<'a>> for TextStyle<'a> {
from(this: &'b TextStyle<'a>) -> Self269     fn from(this: &'b TextStyle<'a>) -> Self {
270         this.clone()
271     }
272 }
273 
274 impl<'a, T: Into<FontDesc<'a>>> From<T> for TextStyle<'a> {
from(font: T) -> Self275     fn from(font: T) -> Self {
276         Self {
277             font: font.into(),
278             color: BLACK.to_backend_color(),
279             pos: text_anchor::Pos::default(),
280         }
281     }
282 }
283 
284 impl<'a> BackendTextStyle for TextStyle<'a> {
285     type FontError = FontError;
color(&self) -> BackendColor286     fn color(&self) -> BackendColor {
287         self.color
288     }
289 
size(&self) -> f64290     fn size(&self) -> f64 {
291         self.font.get_size()
292     }
293 
transform(&self) -> FontTransform294     fn transform(&self) -> FontTransform {
295         self.font.get_transform()
296     }
297 
style(&self) -> FontStyle298     fn style(&self) -> FontStyle {
299         self.font.get_style()
300     }
301 
302     #[allow(clippy::type_complexity)]
layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError>303     fn layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError> {
304         self.font.layout_box(text)
305     }
306 
anchor(&self) -> text_anchor::Pos307     fn anchor(&self) -> text_anchor::Pos {
308         self.pos
309     }
310 
family(&self) -> FontFamily311     fn family(&self) -> FontFamily {
312         self.font.get_family()
313     }
314 
draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>( &self, text: &str, pos: BackendCoord, mut draw: DrawFunc, ) -> Result<Result<(), E>, Self::FontError>315     fn draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>(
316         &self,
317         text: &str,
318         pos: BackendCoord,
319         mut draw: DrawFunc,
320     ) -> Result<Result<(), E>, Self::FontError> {
321         let color = self.color.color();
322         self.font.draw(text, pos, move |x, y, a| {
323             let mix_color = color.mix(a as f64);
324             draw(x, y, mix_color)
325         })
326     }
327 }
328