plotters_backend/
text.rs

1use super::{BackendColor, BackendCoord};
2use std::error::Error;
3
4/// Describes font family.
5/// This can be either a specific font family name, such as "arial",
6/// or a general font family class, such as "serif" and "sans-serif"
7#[derive(Clone, Copy)]
8pub enum FontFamily<'a> {
9    /// The system default serif font family
10    Serif,
11    /// The system default sans-serif font family
12    SansSerif,
13    /// The system default monospace font
14    Monospace,
15    /// A specific font family name
16    Name(&'a str),
17}
18
19impl<'a> FontFamily<'a> {
20    /// Make a CSS compatible string for the font family name.
21    /// This can be used as the value of `font-family` attribute in SVG.
22    pub fn as_str(&self) -> &str {
23        match self {
24            FontFamily::Serif => "serif",
25            FontFamily::SansSerif => "sans-serif",
26            FontFamily::Monospace => "monospace",
27            FontFamily::Name(face) => face,
28        }
29    }
30}
31
32impl<'a> From<&'a str> for FontFamily<'a> {
33    fn from(from: &'a str) -> FontFamily<'a> {
34        match from.to_lowercase().as_str() {
35            "serif" => FontFamily::Serif,
36            "sans-serif" => FontFamily::SansSerif,
37            "monospace" => FontFamily::Monospace,
38            _ => FontFamily::Name(from),
39        }
40    }
41}
42
43/// Text anchor attributes are used to properly position the text.
44///
45/// # Examples
46///
47/// In the example below, the text anchor (X) position is `Pos::new(HPos::Right, VPos::Center)`.
48/// ```text
49///    ***** X
50/// ```
51/// The position is always relative to the text regardless of its rotation.
52/// In the example below, the text has style
53/// `style.transform(FontTransform::Rotate90).pos(Pos::new(HPos::Center, VPos::Top))`.
54/// ```text
55///        *
56///        *
57///        * X
58///        *
59///        *
60/// ```
61pub mod text_anchor {
62    /// The horizontal position of the anchor point relative to the text.
63    #[derive(Clone, Copy, Default)]
64    pub enum HPos {
65        /// Anchor point is on the left side of the text
66        #[default]
67        Left,
68        /// Anchor point is on the right side of the text
69        Right,
70        /// Anchor point is in the horizontal center of the text
71        Center,
72    }
73
74    /// The vertical position of the anchor point relative to the text.
75    #[derive(Clone, Copy, Default)]
76    pub enum VPos {
77        /// Anchor point is on the top of the text
78        #[default]
79        Top,
80        /// Anchor point is in the vertical center of the text
81        Center,
82        /// Anchor point is on the bottom of the text
83        Bottom,
84    }
85
86    /// The text anchor position.
87    #[derive(Clone, Copy, Default)]
88    pub struct Pos {
89        /// The horizontal position of the anchor point
90        pub h_pos: HPos,
91        /// The vertical position of the anchor point
92        pub v_pos: VPos,
93    }
94
95    impl Pos {
96        /// Create a new text anchor position.
97        ///
98        /// - `h_pos`: The horizontal position of the anchor point
99        /// - `v_pos`: The vertical position of the anchor point
100        /// - **returns** The newly created text anchor position
101        ///
102        /// ```rust
103        /// use plotters_backend::text_anchor::{Pos, HPos, VPos};
104        ///
105        /// let pos = Pos::new(HPos::Left, VPos::Top);
106        /// ```
107        pub fn new(h_pos: HPos, v_pos: VPos) -> Self {
108            Pos { h_pos, v_pos }
109        }
110    }
111}
112
113/// Specifying text transformations
114#[derive(Clone)]
115pub enum FontTransform {
116    /// Nothing to transform
117    None,
118    /// Rotating the text 90 degree clockwise
119    Rotate90,
120    /// Rotating the text 180 degree clockwise
121    Rotate180,
122    /// Rotating the text 270 degree clockwise
123    Rotate270,
124}
125
126impl FontTransform {
127    /// Transform the coordinate to perform the rotation
128    ///
129    /// - `x`: The x coordinate in pixels before transform
130    /// - `y`: The y coordinate in pixels before transform
131    /// - **returns**: The coordinate after transform
132    pub fn transform(&self, x: i32, y: i32) -> (i32, i32) {
133        match self {
134            FontTransform::None => (x, y),
135            FontTransform::Rotate90 => (-y, x),
136            FontTransform::Rotate180 => (-x, -y),
137            FontTransform::Rotate270 => (y, -x),
138        }
139    }
140}
141
142/// Describes the font style. Such as Italic, Oblique, etc.
143#[derive(Clone, Copy)]
144pub enum FontStyle {
145    /// The normal style
146    Normal,
147    /// The oblique style
148    Oblique,
149    /// The italic style
150    Italic,
151    /// The bold style
152    Bold,
153}
154
155impl FontStyle {
156    /// Convert the font style into a CSS compatible string which can be used in `font-style` attribute.
157    pub fn as_str(&self) -> &str {
158        match self {
159            FontStyle::Normal => "normal",
160            FontStyle::Italic => "italic",
161            FontStyle::Oblique => "oblique",
162            FontStyle::Bold => "bold",
163        }
164    }
165}
166
167impl<'a> From<&'a str> for FontStyle {
168    fn from(from: &'a str) -> FontStyle {
169        match from.to_lowercase().as_str() {
170            "normal" => FontStyle::Normal,
171            "italic" => FontStyle::Italic,
172            "oblique" => FontStyle::Oblique,
173            "bold" => FontStyle::Bold,
174            _ => FontStyle::Normal,
175        }
176    }
177}
178
179/// The trait that abstracts a style of a text.
180///
181/// This is used because the the backend crate have no knowledge about how
182/// the text handling is implemented in plotters.
183///
184/// But the backend still wants to know some information about the font, for
185/// the backend doesn't handles text drawing, may want to call the `draw` method which
186/// is implemented by the plotters main crate. While for the backend that handles the
187/// text drawing, those font information provides instructions about how the text should be
188/// rendered: color, size, slant, anchor, font, etc.
189///
190/// This trait decouples the detailed implementation about the font and the backend code which
191/// wants to perform some operation on the font.
192///
193pub trait BackendTextStyle {
194    /// The error type of this text style implementation
195    type FontError: Error + Sync + Send + 'static;
196
197    fn color(&self) -> BackendColor {
198        BackendColor {
199            alpha: 1.0,
200            rgb: (0, 0, 0),
201        }
202    }
203
204    fn size(&self) -> f64 {
205        1.0
206    }
207
208    fn transform(&self) -> FontTransform {
209        FontTransform::None
210    }
211
212    fn style(&self) -> FontStyle {
213        FontStyle::Normal
214    }
215
216    fn anchor(&self) -> text_anchor::Pos {
217        text_anchor::Pos::default()
218    }
219
220    fn family(&self) -> FontFamily;
221
222    #[allow(clippy::type_complexity)]
223    fn layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError>;
224
225    fn draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>(
226        &self,
227        text: &str,
228        pos: BackendCoord,
229        draw: DrawFunc,
230    ) -> Result<Result<(), E>, Self::FontError>;
231}