use embedded_graphics::text::{
renderer::{CharacterStyle, TextMetrics, TextRenderer},
Baseline,
};
use embedded_graphics_core::{
prelude::{DrawTarget, PixelColor, Point, Size},
primitives::Rectangle,
};
use crate::{
types::{FontColor, VerticalPosition},
Error, Font, FontRenderer,
};
#[cfg_attr(docsrs, doc(cfg(feature = "embedded_graphics_textstyle")))]
impl From<Baseline> for VerticalPosition {
fn from(baseline: Baseline) -> Self {
match baseline {
Baseline::Top => VerticalPosition::Top,
Baseline::Bottom => VerticalPosition::Bottom,
Baseline::Middle => VerticalPosition::Center,
Baseline::Alphabetic => VerticalPosition::Baseline,
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "embedded_graphics_textstyle")))]
#[derive(Debug, Clone)]
pub struct U8g2TextStyle<C> {
pub text_color: Option<C>,
pub background_color: Option<C>,
font: FontRenderer,
}
impl<C> U8g2TextStyle<C> {
pub fn new<F: Font>(font: F, text_color: C) -> Self {
drop(font);
Self {
text_color: Some(text_color),
background_color: None,
font: FontRenderer::new::<F>().with_ignore_unknown_chars(true),
}
}
}
impl<C> TextRenderer for U8g2TextStyle<C>
where
C: PixelColor,
{
type Color = C;
fn draw_string<D>(
&self,
text: &str,
position: Point,
baseline: Baseline,
target: &mut D,
) -> Result<Point, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
let mut adjusted_position = position;
if let Baseline::Alphabetic = baseline {
adjusted_position.y += 1;
}
let result;
if let Some(text_color) = self.text_color {
let color = match self.background_color {
Some(background_color) => FontColor::WithBackground {
fg: text_color,
bg: background_color,
},
None => FontColor::Transparent(text_color),
};
result = self
.font
.render(text, adjusted_position, baseline.into(), color, target);
} else {
result = self
.font
.get_rendered_dimensions(text, adjusted_position, baseline.into())
.map_err(Into::into);
};
result
.map_err(unwrap_error)
.map(|dims| position + dims.advance)
}
fn draw_whitespace<D>(
&self,
width: u32,
position: Point,
baseline: Baseline,
target: &mut D,
) -> Result<Point, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
if width != 0 {
if let Some(color) = self.background_color {
let whitespace_dimensions = self.font
.get_rendered_dimensions(' ', position, baseline.into())
.expect("Internal error: Should never happen because the font reader is configured to ignore unknown characters.");
if let Some(bounding_box) = whitespace_dimensions.bounding_box {
let top_left = bounding_box.top_left;
let height = bounding_box.size.height;
target
.fill_solid(&Rectangle::new(top_left, Size::new(width, height)), color)?;
}
}
}
Ok(position + Size::new(width, 0))
}
fn measure_string(&self, text: &str, position: Point, baseline: Baseline) -> TextMetrics {
let mut adjusted_position = position;
if let Baseline::Alphabetic = baseline {
adjusted_position.y += 1;
}
let dims = self
.font
.get_rendered_dimensions(text, adjusted_position, baseline.into())
.unwrap();
TextMetrics {
bounding_box: dims.bounding_box.unwrap_or_default(),
next_position: position + dims.advance,
}
}
fn line_height(&self) -> u32 {
self.font.get_default_line_height()
}
}
impl<C> CharacterStyle for U8g2TextStyle<C>
where
C: PixelColor,
{
type Color = C;
fn set_text_color(&mut self, text_color: Option<Self::Color>) {
self.text_color = text_color;
}
fn set_background_color(&mut self, background_color: Option<Self::Color>) {
self.background_color = background_color;
}
}
impl<C> TextRenderer for &U8g2TextStyle<C>
where
C: PixelColor,
{
type Color = C;
fn draw_string<D>(
&self,
text: &str,
position: Point,
baseline: Baseline,
target: &mut D,
) -> Result<Point, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
(*self).draw_string(text, position, baseline, target)
}
fn draw_whitespace<D>(
&self,
width: u32,
position: Point,
baseline: Baseline,
target: &mut D,
) -> Result<Point, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
(*self).draw_whitespace(width, position, baseline, target)
}
fn measure_string(&self, text: &str, position: Point, baseline: Baseline) -> TextMetrics {
(*self).measure_string(text, position, baseline)
}
fn line_height(&self) -> u32 {
(*self).line_height()
}
}
fn unwrap_error<E>(err: Error<E>) -> E {
match err {
Error::DisplayError(e) => e,
Error::BackgroundColorNotSupported => {
panic!("Background color not supported for this font!")
}
Error::GlyphNotFound(_) => unreachable!(),
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::println;
use embedded_graphics::{pixelcolor::Rgb888, prelude::RgbColor};
use crate::fonts;
use super::*;
fn examine<T: Clone + core::fmt::Debug>(val: T) {
println!("{:?}", val.clone());
}
#[test]
fn is_debug_and_clone() {
examine(U8g2TextStyle::new(fonts::u8g2_font_10x20_mf, Rgb888::RED));
}
#[test]
#[should_panic(expected = "internal error: entered unreachable code")]
fn dummy_test_unreachable() {
unwrap_error(Error::<&str>::GlyphNotFound('a'));
}
}