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 | ![](https://cdn.jsdelivr.net/gh/facorread/plotters-doc-data@f030ed3/apidoc/font_desc_color.svg) |
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 | |