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  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  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  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