| 1 | use super::{FontData, FontDataInternal}; |
| 2 | use crate::style::text_anchor::Pos; |
| 3 | use crate::style::{Color, TextStyle}; |
| 4 | |
| 5 | use std::convert::From; |
| 6 | |
| 7 | pub use plotters_backend::{FontFamily, FontStyle, FontTransform}; |
| 8 | |
| 9 | /// The error type for the font implementation |
| 10 | pub type FontError = <FontDataInternal as FontData>::ErrorType; |
| 11 | |
| 12 | /// The type we used to represent a result of any font operations |
| 13 | pub type FontResult<T> = Result<T, FontError>; |
| 14 | |
| 15 | /// Describes a font |
| 16 | #[derive(Clone)] |
| 17 | pub struct FontDesc<'a> { |
| 18 | size: f64, |
| 19 | family: FontFamily<'a>, |
| 20 | data: FontResult<FontDataInternal>, |
| 21 | transform: FontTransform, |
| 22 | style: FontStyle, |
| 23 | } |
| 24 | |
| 25 | impl<'a> FontDesc<'a> { |
| 26 | /// Create a new font |
| 27 | /// |
| 28 | /// - `family`: The font family name |
| 29 | /// - `size`: The size of the font |
| 30 | /// - `style`: The font variations |
| 31 | /// - **returns** The newly created font description |
| 32 | pub fn new(family: FontFamily<'a>, size: f64, style: FontStyle) -> Self { |
| 33 | Self { |
| 34 | size, |
| 35 | family, |
| 36 | data: FontDataInternal::new(family, style), |
| 37 | transform: FontTransform::None, |
| 38 | style, |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | /// Create a new font desc with the same font but different size |
| 43 | /// |
| 44 | /// - `size`: The new size to set |
| 45 | /// - **returns** The newly created font descriptor with a new size |
| 46 | pub fn resize(&self, size: f64) -> Self { |
| 47 | Self { |
| 48 | size, |
| 49 | family: self.family, |
| 50 | data: self.data.clone(), |
| 51 | transform: self.transform.clone(), |
| 52 | style: self.style, |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | /// Set the style of the font |
| 57 | /// |
| 58 | /// - `style`: The new style |
| 59 | /// - **returns** The new font description with this style applied |
| 60 | pub fn style(&self, style: FontStyle) -> Self { |
| 61 | Self { |
| 62 | size: self.size, |
| 63 | family: self.family, |
| 64 | data: self.data.clone(), |
| 65 | transform: self.transform.clone(), |
| 66 | style, |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /// Set the font transformation |
| 71 | /// |
| 72 | /// - `trans`: The new transformation |
| 73 | /// - **returns** The new font description with this font transformation applied |
| 74 | pub fn transform(&self, trans: FontTransform) -> Self { |
| 75 | Self { |
| 76 | size: self.size, |
| 77 | family: self.family, |
| 78 | data: self.data.clone(), |
| 79 | transform: trans, |
| 80 | style: self.style, |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | /// Get the font transformation description |
| 85 | pub fn get_transform(&self) -> FontTransform { |
| 86 | self.transform.clone() |
| 87 | } |
| 88 | |
| 89 | /** Returns a new text style object with the specified `color`. |
| 90 | |
| 91 | # Example |
| 92 | |
| 93 | ``` |
| 94 | use plotters::prelude::*; |
| 95 | let text_style = ("sans-serif" , 20).into_font().color(&RED); |
| 96 | let drawing_area = SVGBackend::new("font_desc_color.svg" , (200, 100)).into_drawing_area(); |
| 97 | drawing_area.fill(&WHITE).unwrap(); |
| 98 | drawing_area.draw_text("This is a big red label" , &text_style, (10, 50)); |
| 99 | ``` |
| 100 | |
| 101 | The result is a text label colorized accordingly: |
| 102 | |
| 103 |  |
| 104 | |
| 105 | # See also |
| 106 | |
| 107 | [`IntoTextStyle::with_color()`](crate::style::IntoTextStyle::with_color) |
| 108 | |
| 109 | [`IntoTextStyle::into_text_style()`](crate::style::IntoTextStyle::into_text_style) for a more succinct example |
| 110 | |
| 111 | */ |
| 112 | pub fn color<C: Color>(&self, color: &C) -> TextStyle<'a> { |
| 113 | TextStyle { |
| 114 | font: self.clone(), |
| 115 | color: color.to_backend_color(), |
| 116 | pos: Pos::default(), |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /// Returns the font family |
| 121 | pub fn get_family(&self) -> FontFamily { |
| 122 | self.family |
| 123 | } |
| 124 | |
| 125 | /// Get the name of the font |
| 126 | pub fn get_name(&self) -> &str { |
| 127 | self.family.as_str() |
| 128 | } |
| 129 | |
| 130 | /// Get the name of the style |
| 131 | pub fn get_style(&self) -> FontStyle { |
| 132 | self.style |
| 133 | } |
| 134 | |
| 135 | /// Get the size of font |
| 136 | pub fn get_size(&self) -> f64 { |
| 137 | self.size |
| 138 | } |
| 139 | |
| 140 | /// Get the size of the text if rendered in this font |
| 141 | /// |
| 142 | /// For a TTF type, zero point of the layout box is the left most baseline char of the string |
| 143 | /// Thus the upper bound of the box is most likely be negative |
| 144 | pub fn layout_box(&self, text: &str) -> FontResult<((i32, i32), (i32, i32))> { |
| 145 | match &self.data { |
| 146 | Ok(ref font) => font.estimate_layout(self.size, text), |
| 147 | Err(e) => Err(e.clone()), |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | /// Get the size of the text if rendered in this font. |
| 152 | /// This is similar to `layout_box` function, but it apply the font transformation |
| 153 | /// and estimate the overall size of the font |
| 154 | pub fn box_size(&self, text: &str) -> FontResult<(u32, u32)> { |
| 155 | let ((min_x, min_y), (max_x, max_y)) = self.layout_box(text)?; |
| 156 | let (w, h) = self.get_transform().transform(max_x - min_x, max_y - min_y); |
| 157 | Ok((w.unsigned_abs(), h.unsigned_abs())) |
| 158 | } |
| 159 | |
| 160 | /// Actually draws a font with a drawing function |
| 161 | pub fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>( |
| 162 | &self, |
| 163 | text: &str, |
| 164 | (x, y): (i32, i32), |
| 165 | draw: DrawFunc, |
| 166 | ) -> FontResult<Result<(), E>> { |
| 167 | match &self.data { |
| 168 | Ok(ref font) => font.draw((x, y), self.size, text, draw), |
| 169 | Err(e) => Err(e.clone()), |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | impl<'a> From<&'a str> for FontDesc<'a> { |
| 175 | fn from(from: &'a str) -> FontDesc<'a> { |
| 176 | FontDesc::new(from.into(), 12.0, FontStyle::Normal) |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | impl<'a> From<FontFamily<'a>> for FontDesc<'a> { |
| 181 | fn from(family: FontFamily<'a>) -> FontDesc<'a> { |
| 182 | FontDesc::new(family, 12.0, FontStyle::Normal) |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | impl<'a, T: Into<f64>> From<(FontFamily<'a>, T)> for FontDesc<'a> { |
| 187 | fn from((family, size): (FontFamily<'a>, T)) -> FontDesc<'a> { |
| 188 | FontDesc::new(family, size.into(), FontStyle::Normal) |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | impl<'a, T: Into<f64>> From<(&'a str, T)> for FontDesc<'a> { |
| 193 | fn from((typeface, size): (&'a str, T)) -> FontDesc<'a> { |
| 194 | FontDesc::new(typeface.into(), size.into(), FontStyle::Normal) |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | impl<'a, T: Into<f64>, S: Into<FontStyle>> From<(FontFamily<'a>, T, S)> for FontDesc<'a> { |
| 199 | fn from((family, size, style): (FontFamily<'a>, T, S)) -> FontDesc<'a> { |
| 200 | FontDesc::new(family, size.into(), style.into()) |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | impl<'a, T: Into<f64>, S: Into<FontStyle>> From<(&'a str, T, S)> for FontDesc<'a> { |
| 205 | fn from((typeface, size, style): (&'a str, T, S)) -> FontDesc<'a> { |
| 206 | FontDesc::new(typeface.into(), size.into(), style.into()) |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /// The trait that allows some type turns into a font description |
| 211 | pub trait IntoFont<'a> { |
| 212 | /// Make the font description from the source type |
| 213 | fn into_font(self) -> FontDesc<'a>; |
| 214 | } |
| 215 | |
| 216 | impl<'a, T: Into<FontDesc<'a>>> IntoFont<'a> for T { |
| 217 | fn into_font(self) -> FontDesc<'a> { |
| 218 | self.into() |
| 219 | } |
| 220 | } |
| 221 | |