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}