Skip to content
16 changes: 12 additions & 4 deletions eg-bdf-examples/examples/font_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fn draw<S: TextRenderer<Color = Rgb888> + Copy>(
draw_text(display, &text);

let position = display.bounding_box().anchor_point(AnchorPoint::BottomLeft)
+ Point::new(5, -(line_height as i32) * 3 / 2);
+ Point::new(5, -(line_height as i32) * 3);

Line::with_delta(position.y_axis(), Point::zero() + display.size().x_axis())
.into_styled(PrimitiveStyle::with_stroke(Rgb888::CSS_DIM_GRAY, 1))
Expand Down Expand Up @@ -130,7 +130,7 @@ fn try_main() -> Result<()> {
.build();

let line_height = bdf_font.ascent + bdf_font.descent;
let display_height = line_height * 8;
let display_height = line_height * 10;
let display_width = (line_height * 25).max(display_height);
let display_size = Size::new(display_width, display_height);

Expand All @@ -142,7 +142,7 @@ fn try_main() -> Result<()> {
let mut window = Window::new("Font viewer", &settings);

let mut use_mono_font = false;
let use_serialized_font = true;
let mut use_serialized_font = false;

'main_loop: loop {
window.update(&display);
Expand All @@ -153,14 +153,22 @@ fn try_main() -> Result<()> {
Keycode::M => {
use_mono_font = !use_mono_font;
}
Keycode::S => {
if use_mono_font {
use_mono_font = false;
use_serialized_font = true;
} else {
use_serialized_font = !use_serialized_font;
}
}
_ => {}
},
SimulatorEvent::Quit => break 'main_loop,
_ => {}
}
}

let mut hint = "Press M to toggle".to_string();
let mut hint = "Press M or S to toggle".to_string();

display.clear(Rgb888::BLACK).unwrap();
if use_mono_font {
Expand Down
24 changes: 23 additions & 1 deletion eg-bdf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use embedded_graphics::{

mod proportional;
mod serialized;
pub use proportional::{ProportionalFont, ProportionalTextStyle};
pub use proportional::{Metrics, ProportionalFont, ProportionalTextStyle};
pub use serialized::{SerializedBdfFont, SerializedBdfTextStyle};

/// BDF font.
Expand Down Expand Up @@ -64,6 +64,28 @@ impl<'a> BdfGlyph {
}
}

impl<'a> ProportionalFont<'a> for BdfFont<'a> {
fn metrics(&self) -> Metrics {
Metrics {
ascent: self.ascent,
descent: self.descent,
line_height: self.ascent + self.descent,
}
}

fn replacement_glyph(&self) -> Option<DisplayBdfGlyph<'_>> {
Some(self.glyphs[self.replacement_character].into_glyph(self))
}

fn lookup(&self, c: char) -> Option<DisplayBdfGlyph<'_>> {
if let Some(&g) = self.glyphs.iter().find(|g| g.character == c) {
Some(g.into_glyph(self))
} else {
None
}
}
}

/// Unserialized BDF text style
pub type BdfTextStyle<'a, C> = ProportionalTextStyle<'a, BdfFont<'a>, C>;

Expand Down
81 changes: 39 additions & 42 deletions eg-bdf/src/proportional.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::{BdfFont, DisplayBdfGlyph};
use embedded_graphics::{
prelude::*,
primitives::Rectangle,
Expand All @@ -8,58 +7,41 @@ use embedded_graphics::{
},
};

use crate::DisplayBdfGlyph;

/// Font metrics.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub struct Metrics {
/// Ascent above the baseline in pixels.
pub ascent: u32,
/// Descent below the baseline in pixels.
pub descent: u32,
/// Line height in pixels.
pub line_height: u32,
}

/// A proportional font
pub trait ProportionalFont<'a>: Clone {
/// Returns a struct containing ascent, descent, baseline_offset, and line_height
fn metrics(&self) -> Metrics;
/// Finds a BdfGlyph for a character
fn lookup(&self, c: char) -> Option<DisplayBdfGlyph<'_>>;
/// Finds the replacement glyph
fn replacement_glyph(&'a self) -> DisplayBdfGlyph<'a>;

/// Returns the baseline offset
impl Metrics {
/// Returns the baseline offset.
fn baseline_offset(&self, baseline: Baseline) -> i32 {
match baseline {
Baseline::Top => self.metrics().ascent.saturating_sub(1) as i32,
Baseline::Bottom => -(self.metrics().descent as i32),
Baseline::Middle => (self.metrics().ascent as i32 - self.metrics().descent as i32) / 2,
Baseline::Top => self.ascent.saturating_sub(1) as i32,
Baseline::Bottom => -(self.descent as i32),
Baseline::Middle => (self.ascent as i32 - self.descent as i32) / 2,
Baseline::Alphabetic => 0,
}
}

/// Returns a glyph, or a replacement character if no corresponding glyph exists
fn glyph_or_replacement(&'a self, c: char) -> DisplayBdfGlyph<'a> {
self.lookup(c).unwrap_or(self.replacement_glyph())
}
}

impl<'a> ProportionalFont<'a> for BdfFont<'a> {
fn metrics(&self) -> Metrics {
Metrics {
ascent: self.ascent,
descent: self.descent,
line_height: self.ascent + self.descent,
}
}
/// A proportional font
pub trait ProportionalFont<'a>: Clone {
/// Returns the font metrics.
fn metrics(&self) -> Metrics;

fn replacement_glyph(&'a self) -> DisplayBdfGlyph<'a> {
self.glyphs[self.replacement_character].into_glyph(self)
}
/// Finds the glyph for the given character.
fn lookup(&self, c: char) -> Option<DisplayBdfGlyph<'_>>;

fn lookup(&self, c: char) -> Option<DisplayBdfGlyph<'_>> {
if let Some(&g) = self.glyphs.iter().find(|g| g.character == c) {
Some(g.into_glyph(self))
} else {
None
}
}
/// Returns the replacement glyph
fn replacement_glyph(&self) -> Option<DisplayBdfGlyph<'_>>;
}

/// A generalized text style for proportional fonts
Expand Down Expand Up @@ -104,10 +86,18 @@ impl<'a, C: PixelColor, F: ProportionalFont<'a>> TextRenderer for ProportionalTe
where
D: DrawTarget<Color = Self::Color>,
{
let mut position = position + Point::new(0, self.font.baseline_offset(baseline));
let metrics = self.font.metrics();
let mut position = position + Point::new(0, metrics.baseline_offset(baseline));

for c in text.chars() {
let glyph = self.font.glyph_or_replacement(c);
let Some(glyph) = self
.font
.lookup(c)
.or_else(|| self.font.replacement_glyph())
else {
// skip invalid characters entirely if replacement glyph isn't available
continue;
};

glyph.draw(position, self.color, target)?;

Expand All @@ -127,17 +117,24 @@ impl<'a, C: PixelColor, F: ProportionalFont<'a>> TextRenderer for ProportionalTe
where
D: DrawTarget<Color = Self::Color>,
{
let position = position + Point::new(0, self.font.baseline_offset(baseline));
let metrics = self.font.metrics();
let position = position + Point::new(0, metrics.baseline_offset(baseline));

Ok(position + Size::new(width, 0))
}

fn measure_string(&self, text: &str, position: Point, baseline: Baseline) -> TextMetrics {
let position = position + Point::new(0, self.font.baseline_offset(baseline));
let metrics = self.font.metrics();
let position = position + Point::new(0, metrics.baseline_offset(baseline));

let dx = text
.chars()
.map(|c| self.font.glyph_or_replacement(c).device_width)
.filter_map(|c| {
self.font
.lookup(c)
.or_else(|| self.font.replacement_glyph())
.map(|g| g.device_width)
})
.sum();

// TODO: calculate correct bounding box
Expand Down
Loading
Loading