embedded_graphics/text/
mod.rs

1//! Text drawing.
2//!
3//! The [`Text`] drawable can be used to draw text on a draw target. To construct a [`Text`] object
4//! at least a text string, position and character style are required. For advanced formatting
5//! options an additional [`TextStyle`] object might be required.
6//!
7//! Text rendering in embedded-graphics is designed to be extendable by text renderers for different
8//! font formats. To use a text renderer in an embedded-graphics project each renderer provides a
9//! character style object. This object is used to set the appearance of characters, like the text
10//! color or the used font. The available settings vary between different text renderer and are
11//! documented in the text renderer documentation.
12//!
13//! See the [`renderer` module] docs for more information about implementing custom text renderers.
14//!
15//! Embedded-graphics includes a text renderer for monospaced fonts in the [`mono_font`] module.
16//! Most examples will use this renderer and the associated [`MonoTextStyle`] character style.
17//! But they should be easily adaptable to any external renderer listed in the
18//! [external crates list].
19//!
20//! # Text style
21//!
22//! In addition to styling the individual characters the [`Text`] drawable also contains a
23//! [`TextStyle`] setting. The text style is used to set the alignment and line spacing of text
24//! objects.
25//!
26//! The [`alignment`] setting sets the horizontal alignment of the text. With the default value
27//! `Left` the text will be rendered to the right of the given text position. Analogously `Right`
28//! aligned text will be rendered to the left of the given position. `Center`ed text will extend
29//! equally to the left and right of the text position.
30//!
31//! The [`baseline`] setting defines the vertical alignment of the first line of text. With the default
32//! setting of `Alphabetic` the glyphs will be drawn with their descenders below the given position.
33//! This means that the bottom of glyphs without descender (like 'A') will be on the same Y
34//! coordinate as the given position. The other baseline settings will position the glyphs relative
35//! to the EM box, without considering the baseline.
36//!
37//! If the text contains multiple lines only the first line will be vertically aligned based on the
38//! baseline setting. All following lines will be spaced relative to the first line, according to the [`line_height`] setting.
39//!
40//! # Examples
41//!
42//! ## Draw basic text
43//!
44//! ```
45//! use embedded_graphics::{
46//!     mono_font::{ascii::FONT_6X10, MonoTextStyle},
47//!     pixelcolor::Rgb565,
48//!     prelude::*,
49//!     text::Text,
50//! };
51//! # use embedded_graphics::mock_display::MockDisplay;
52//! # let mut display: MockDisplay<Rgb565> = MockDisplay::default();
53//! # display.set_allow_out_of_bounds_drawing(true);
54//!
55//! // Create a new character style
56//! let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
57//!
58//! // Create a text at position (20, 30) and draw it using the previously defined style
59//! Text::new("Hello Rust!", Point::new(20, 30), style).draw(&mut display)?;
60//! # Ok::<(), core::convert::Infallible>(())
61//! ```
62//! ## Draw centered text
63//!
64//! [`Text`] provides the [`with_alignment`] and [`with_baseline`] constructors to easily set
65//! these commonly used settings without having to build a [`TextStyle`] object first.
66//!
67//! ```
68//! use embedded_graphics::{
69//!     mono_font::{ascii::FONT_6X10, MonoTextStyle},
70//!     pixelcolor::Rgb565,
71//!     prelude::*,
72//!     text::{Text, Alignment},
73//! };
74//! # use embedded_graphics::mock_display::MockDisplay;
75//! # let mut display: MockDisplay<Rgb565> = MockDisplay::default();
76//! # display.set_allow_out_of_bounds_drawing(true);
77//!
78//! // Create a new character style
79//! let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
80//!
81//! // Create a text at position (20, 30) and draw it using the previously defined style
82//! Text::with_alignment(
83//!     "First line\nSecond line",
84//!     Point::new(20, 30),
85//!     style,
86//!     Alignment::Center,
87//! )
88//! .draw(&mut display)?;
89//! # Ok::<(), core::convert::Infallible>(())
90//! ```
91//!
92//! ## Draw text with `TextStyle`
93//!
94//! For more advanced text styles a [`TextStyle`] object can be build using the
95//! [`TextStyleBuilder`] and then passed to the [`with_text_style`] constructor.
96//!
97//! ```
98//! use embedded_graphics::{
99//!     mono_font::{ascii::FONT_6X10, MonoTextStyle},
100//!     pixelcolor::Rgb565,
101//!     prelude::*,
102//!     text::{Alignment, LineHeight, Text, TextStyleBuilder},
103//! };
104//! # use embedded_graphics::mock_display::MockDisplay;
105//! # let mut display: MockDisplay<Rgb565> = MockDisplay::default();
106//! # display.set_allow_out_of_bounds_drawing(true);
107//!
108//! // Create a new character style.
109//! let character_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
110//!
111//! // Create a new text style.
112//! let text_style = TextStyleBuilder::new()
113//!     .alignment(Alignment::Center)
114//!     .line_height(LineHeight::Percent(150))
115//!     .build();
116//!
117//! // Create a text at position (20, 30) and draw it using the previously defined style.
118//! Text::with_text_style(
119//!     "First line\nSecond line",
120//!     Point::new(20, 30),
121//!     character_style,
122//!     text_style,
123//! )
124//! .draw(&mut display)?;
125//! # Ok::<(), core::convert::Infallible>(())
126//! ```
127//!
128//! ## Combine different character styles
129//!
130//! The `draw` method for text drawables returns the position of the next character. This can be
131//! used to combine text with different character styles on a single line of text.
132//!
133//! ```
134//! use embedded_graphics::{
135//!     mono_font::{ascii::{FONT_6X10, FONT_10X20}, MonoTextStyle},
136//!     pixelcolor::Rgb565,
137//!     prelude::*,
138//!     text::{Alignment, LineHeight, Text, TextStyleBuilder},
139//! };
140//! # use embedded_graphics::mock_display::MockDisplay;
141//! # let mut display: MockDisplay<Rgb565> = MockDisplay::default();
142//! # display.set_allow_out_of_bounds_drawing(true);
143//!
144//! // Create a small and a large character style.
145//! let small_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
146//! let large_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
147//!
148//! // Draw the first text at (20, 30) using the small character style.
149//! let next = Text::new("small ", Point::new(20, 30), small_style).draw(&mut display)?;
150//!
151//! // Draw the second text after the first text using the large character style.
152//! let next = Text::new("large", next, large_style).draw(&mut display)?;
153//! # Ok::<(), core::convert::Infallible>(())
154//! ```
155//!
156//! [`Text::new`]: TextStyle::new()
157//! [`with_alignment`]: Text::with_alignment()
158//! [`with_baseline`]: Text::with_baseline()
159//! [`with_text_style`]: Text::with_text_style()
160//! [`alignment`]: TextStyle::alignment
161//! [`baseline`]: TextStyle::baseline
162//! [`line_height`]: TextStyle::line_height
163//! [`mono_font`]: super::mono_font
164//! [`MonoTextStyle`]: super::mono_font::MonoTextStyle
165//! [`renderer` module]: renderer
166//! [external crates list]: super#additional-functions-provided-by-external-crates
167
168pub mod renderer;
169mod text;
170mod text_style;
171
172use embedded_graphics_core::prelude::PixelColor;
173pub use text::Text;
174pub use text_style::{TextStyle, TextStyleBuilder};
175
176/// Text baseline.
177#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
178#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
179pub enum Baseline {
180    /// Top.
181    Top,
182    /// Bottom.
183    Bottom,
184    /// Middle.
185    Middle,
186    /// Alphabetic baseline.
187    Alphabetic,
188}
189
190/// Horizontal text alignment.
191#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
192#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
193pub enum Alignment {
194    /// Left.
195    Left,
196    /// Center.
197    Center,
198    /// Right.
199    Right,
200}
201
202/// Text decoration color.
203#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
204#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
205pub enum DecorationColor<C> {
206    /// No text decoration.
207    None,
208    /// Text decoration with the same color as the text.
209    TextColor,
210    /// Text decoration with a custom color.
211    Custom(C),
212}
213
214impl<C: PixelColor> DecorationColor<C> {
215    /// Returns `true` if the decoration_color is `None`.
216    pub const fn is_none(&self) -> bool {
217        matches!(self, Self::None)
218    }
219
220    /// Returns `true` if the decoration_color is `TextColor`.
221    pub const fn is_text_color(&self) -> bool {
222        matches!(self, Self::TextColor)
223    }
224
225    /// Returns `true` if the decoration_color is `Custom`.
226    pub const fn is_custom(&self) -> bool {
227        matches!(self, Self::Custom(_))
228    }
229
230    pub(crate) const fn to_color(&self, text_color: Option<C>) -> Option<C> {
231        match self {
232            DecorationColor::TextColor => text_color,
233            DecorationColor::Custom(custom_color) => Some(*custom_color),
234            DecorationColor::None => None,
235        }
236    }
237}
238
239/// Text line height.
240///
241/// The line height is defined as the vertical distance between the baseline of two adjacent lines
242/// of text.
243#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
244#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
245pub enum LineHeight {
246    /// Absolute line height in pixels.
247    Pixels(u32),
248
249    /// Relative line height in percent of the default line height.
250    Percent(u32),
251}
252
253impl LineHeight {
254    /// Converts the line height to an absolute pixel distance.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use embedded_graphics::text::LineHeight;
260    ///
261    /// let relative_height = LineHeight::Percent(150);
262    /// assert_eq!(relative_height.to_absolute(20), 30);
263    /// ```
264    pub const fn to_absolute(self, base_line_height: u32) -> u32 {
265        match self {
266            Self::Pixels(px) => px,
267            Self::Percent(percent) => base_line_height * percent / 100,
268        }
269    }
270}
271
272impl Default for LineHeight {
273    fn default() -> Self {
274        Self::Percent(100)
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use crate::pixelcolor::BinaryColor;
282
283    #[test]
284    fn decoration_color_is_methods() {
285        let none = DecorationColor::<BinaryColor>::None;
286        assert!(none.is_none());
287        assert!(!none.is_text_color());
288        assert!(!none.is_custom());
289
290        let text_color = DecorationColor::<BinaryColor>::TextColor;
291        assert!(!text_color.is_none());
292        assert!(text_color.is_text_color());
293        assert!(!text_color.is_custom());
294
295        let custom = DecorationColor::Custom(BinaryColor::On);
296        assert!(!custom.is_none());
297        assert!(!custom.is_text_color());
298        assert!(custom.is_custom());
299    }
300
301    #[test]
302    fn line_height_to_absolute() {
303        assert_eq!(LineHeight::Pixels(100).to_absolute(20), 100);
304        assert_eq!(LineHeight::Percent(100).to_absolute(20), 20);
305        assert_eq!(LineHeight::Percent(150).to_absolute(20), 30);
306    }
307}