diff --git a/eg-bdf-examples/examples/font_viewer.rs b/eg-bdf-examples/examples/font_viewer.rs index 989bb9e..5b0176c 100644 --- a/eg-bdf-examples/examples/font_viewer.rs +++ b/eg-bdf-examples/examples/font_viewer.rs @@ -57,7 +57,7 @@ fn draw + 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)) @@ -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); @@ -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); @@ -153,6 +153,14 @@ 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, @@ -160,7 +168,7 @@ fn try_main() -> Result<()> { } } - 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 { diff --git a/eg-bdf/src/lib.rs b/eg-bdf/src/lib.rs index 4c88f9a..6ac9647 100644 --- a/eg-bdf/src/lib.rs +++ b/eg-bdf/src/lib.rs @@ -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. @@ -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> { + Some(self.glyphs[self.replacement_character].into_glyph(self)) + } + + fn lookup(&self, c: char) -> Option> { + 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>; diff --git a/eg-bdf/src/proportional.rs b/eg-bdf/src/proportional.rs index d35b37c..f47aa9f 100644 --- a/eg-bdf/src/proportional.rs +++ b/eg-bdf/src/proportional.rs @@ -1,4 +1,3 @@ -use crate::{BdfFont, DisplayBdfGlyph}; use embedded_graphics::{ prelude::*, primitives::Rectangle, @@ -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>; - /// 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>; - fn lookup(&self, c: char) -> Option> { - 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>; } /// A generalized text style for proportional fonts @@ -104,10 +86,18 @@ impl<'a, C: PixelColor, F: ProportionalFont<'a>> TextRenderer for ProportionalTe where D: DrawTarget, { - 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)?; @@ -127,17 +117,24 @@ impl<'a, C: PixelColor, F: ProportionalFont<'a>> TextRenderer for ProportionalTe where D: DrawTarget, { - 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 diff --git a/eg-bdf/src/serialized.rs b/eg-bdf/src/serialized.rs index e811f1d..4bc6526 100644 --- a/eg-bdf/src/serialized.rs +++ b/eg-bdf/src/serialized.rs @@ -1,6 +1,25 @@ use crate::{DisplayBdfGlyph, ProportionalFont, ProportionalTextStyle}; use embedded_graphics::{prelude::*, primitives::Rectangle}; +// Structure Sizes +const HEADER_SIZE: usize = 12; +const GLYPH_TABLE_ENTRY_SIZE: usize = 18; + +// Offsets for the header +const ASCENT_OFFSET: usize = 0; +const DESCENT_OFFSET: usize = 2; +const REPLACEMENT_OFFSET: usize = 4; +const GLYPH_COUNT_OFFSET: usize = 8; + +// Offsets for the glyph table entries +const CODEPOINT_OFFSET: usize = 0; +const TOPLEFTX_OFFSET: usize = 4; +const TOPLEFTY_OFFSET: usize = 6; +const SIZEX_OFFSET: usize = 8; +const SIZEY_OFFSET: usize = 10; +const DEVICE_WIDTH_OFFSET: usize = 12; +const IDX_OFFSET: usize = 14; + const fn get_be_i16(data: &[u8], idx: usize) -> i16 { i16::from_be_bytes([data[idx], data[idx + 1]]) } @@ -13,72 +32,54 @@ const fn get_be_u32(data: &[u8], idx: usize) -> u32 { u32::from_be_bytes([data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]) } +/// Serialized BDF font. +/// +/// # Binary Format +/// +/// All integers are stored in big endian byte order. +/// /// * Header (12 Bytes): -/// - Ascent (pixels, u16 BE) -/// - Descent (pixels, u16 BE) -/// - Replacement Character (index into character table, u32 BE) -/// - Character Table Length (entries, u32 BE) +/// * Ascent (pixels, `u16`) +/// * Descent (pixels, `u16`) +/// * Replacement Character (index into character table, `u32`) +/// * Glyph Table Length (number of entries, `u32`) /// -/// * Glyph Table (17 Bytes Per Entry): -/// - corresponding codepoint (u32 BE) -/// - top_left.x (i16 BE) -/// - top_left.y (i16 BE) -/// - size.width (u16 BE) -/// - size.height (u16 BE) -/// - device_width (pixels, u8) -/// - data index (bytes from start of data, u32 BE) +/// * Glyph Table (18 Bytes Per Entry): +/// * corresponding codepoint (`u32`) +/// * `top_left.x` (`i16`) +/// * `top_left.y` (`i16`) +/// * `size.width` (`u16`) +/// * `size.height` (`u16`) +/// * `device_width` (pixels, `u16`) +/// * data index (bytes from start of data, `u32`) /// -/// Font bitmap data is stored afterwards +/// The bitmap data for all glyphs is stored after the header and glyph table. Each glyph bitmap +/// starts at a byte boundary. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SerializedBdfFont<'a> { /// The raw u8 data of the serialized font data: &'a [u8], } + impl<'a> SerializedBdfFont<'a> { - // Structure Sizes - const HEADER_SIZE: usize = 12; - const CHARACTER_TABLE_ENTRY_SIZE: usize = 17; - - // Offsets for the header - const ASCENT_OFFSET: usize = 0; - const DESCENT_OFFSET: usize = 2; - const REPLACEMENT_OFFSET: usize = 4; - const CHAR_TABLE_LEN_OFFSET: usize = 8; - - // Offsets for the character table entries - const CODEPOINT_OFFSET: usize = 0; - const TOPLEFTX_OFFSET: usize = 4; - const TOPLEFTY_OFFSET: usize = 6; - const SIZEX_OFFSET: usize = 8; - const SIZEY_OFFSET: usize = 10; - const KERN_OFFSET: usize = 12; - const IDX_OFFSET: usize = 13; - - const fn character_table_data( - &self, - index: usize, - ) -> Option<&[u8; SerializedBdfFont::CHARACTER_TABLE_ENTRY_SIZE]> { + const fn glyph_data(&self, index: usize) -> Option<&[u8; GLYPH_TABLE_ENTRY_SIZE]> { let (_header, data) = self .data - .split_at(Self::HEADER_SIZE + (index * Self::CHARACTER_TABLE_ENTRY_SIZE)); + .split_at(HEADER_SIZE + (index * GLYPH_TABLE_ENTRY_SIZE)); data.first_chunk() } /// Verifies data in a way that prevents panics and returns a serialized font if the data is valid - /// - /// TODO: Make this const - pub fn new(data: &'a [u8]) -> Result { + pub const fn new(data: &'a [u8]) -> Result { // No header - if data.len() < Self::HEADER_SIZE { + if data.len() < HEADER_SIZE { return Err("No header"); } // Safe to construct a font and index its header let font = Self { data }; - // Character table length invalid - - // Character table too small + // Glyph table too small if data.len() < font.header_size() { return Err("No metadata"); } @@ -87,29 +88,35 @@ impl<'a> SerializedBdfFont<'a> { Ok(font) } - /// Returns the length of the glyph table - pub const fn character_count(self) -> u32 { - get_be_u32(self.data, Self::CHAR_TABLE_LEN_OFFSET) + /// Returns the number of glyphs. + pub const fn glyph_count(self) -> usize { + get_be_u32(self.data, GLYPH_COUNT_OFFSET) as usize } /// Returns the offset of the data block const fn header_size(self) -> usize { - Self::HEADER_SIZE + ((self.character_count() as usize) * Self::CHARACTER_TABLE_ENTRY_SIZE) + HEADER_SIZE + self.glyph_count() * GLYPH_TABLE_ENTRY_SIZE } - /// Returns a BdfGlyph in the glyph table - pub fn character_table(self, idx: usize) -> Option> { - let ctd = self.character_table_data(idx)?; - let corresponding_character = char::from_u32(get_be_u32(ctd, Self::CODEPOINT_OFFSET)); - let top_left_x = get_be_i16(ctd, Self::TOPLEFTX_OFFSET); - let top_left_y = get_be_i16(ctd, Self::TOPLEFTY_OFFSET); - let width = get_be_u16(ctd, Self::SIZEX_OFFSET); - let height = get_be_u16(ctd, Self::SIZEY_OFFSET); - let kerning = ctd[Self::KERN_OFFSET]; - let data_index = get_be_u32(ctd, Self::IDX_OFFSET); + /// Gets the glyph for the given index. + /// + /// Returns [`None`] if the index is out of bounds. + pub fn lookup_by_index(self, index: usize) -> Option> { + let glyph = self.glyph_data(index)?; + let corresponding_character = char::from_u32(get_be_u32(glyph, CODEPOINT_OFFSET))?; + let top_left_x = get_be_i16(glyph, TOPLEFTX_OFFSET); + let top_left_y = get_be_i16(glyph, TOPLEFTY_OFFSET); + let width = get_be_u16(glyph, SIZEX_OFFSET); + let height = get_be_u16(glyph, SIZEY_OFFSET); + let device_width = get_be_u16(glyph, DEVICE_WIDTH_OFFSET); + + let data_index = get_be_u32(glyph, IDX_OFFSET); + let bitmap_data = self + .data + .get((self.header_size() + data_index as usize)..)?; Some(DisplayBdfGlyph { - character: corresponding_character.unwrap(), + character: corresponding_character, bounding_box: Rectangle { top_left: Point { x: i32::from(top_left_x), @@ -120,37 +127,37 @@ impl<'a> SerializedBdfFont<'a> { height: u32::from(height), }, }, - device_width: u32::from(kerning), - bitmap_data: &self - .data - .get((self.header_size() + data_index as usize)..)?, + device_width: u32::from(device_width), + bitmap_data, }) } } + impl<'a> ProportionalFont<'a> for SerializedBdfFont<'a> { fn metrics(&self) -> crate::proportional::Metrics { crate::proportional::Metrics { - ascent: u32::from(get_be_u16(self.data, Self::ASCENT_OFFSET)), - descent: u32::from(get_be_u16(self.data, Self::DESCENT_OFFSET)), - line_height: u32::from(get_be_u16(self.data, Self::ASCENT_OFFSET)) - + u32::from(get_be_u16(self.data, Self::DESCENT_OFFSET)), + ascent: u32::from(get_be_u16(self.data, ASCENT_OFFSET)), + descent: u32::from(get_be_u16(self.data, DESCENT_OFFSET)), + line_height: u32::from(get_be_u16(self.data, ASCENT_OFFSET)) + + u32::from(get_be_u16(self.data, DESCENT_OFFSET)), } } - fn replacement_glyph(&self) -> DisplayBdfGlyph<'_> { - let rpos = get_be_u32(self.data, Self::REPLACEMENT_OFFSET); - self.character_table(rpos as usize) - .expect("Replacement character isn't valid") + fn replacement_glyph(&self) -> Option> { + let rpos = get_be_u32(self.data, REPLACEMENT_OFFSET); + self.lookup_by_index(rpos as usize) } fn lookup(&self, c: char) -> Option> { // TODO, make this a binary search - for i in 0..(self.character_count() as usize) { - let tested_character = self - .character_table(i) - .expect("Character table is corrupted"); - if tested_character.character == c { - return Some(tested_character); + for i in 0..self.glyph_count() { + let Some(glyph) = self.lookup_by_index(i) else { + // skip invalid glyphs + continue; + }; + + if glyph.character == c { + return Some(glyph); } } @@ -160,3 +167,108 @@ impl<'a> ProportionalFont<'a> for SerializedBdfFont<'a> { /// Stylized serialized BDF text pub type SerializedBdfTextStyle<'a, C> = ProportionalTextStyle<'a, SerializedBdfFont<'a>, C>; + +#[cfg(test)] +mod tests { + use embedded_graphics::{mock_display::MockDisplay, pixelcolor::BinaryColor, text::Text}; + + use crate::proportional::Metrics; + + use super::*; + + #[rustfmt::skip] + const TEST_FONT: &[u8] = &[ + // header + 0x00, 0x04, // ascent = 4 + 0x00, 0x01, // descent = 1 + 0x00, 0x00, 0x00, 0x01, // replacement glyph at index 1 + 0x00, 0x00, 0x00, 0x02, // 2 glyphs + + // 'a' glyph + 0x00, 0x00, 0x00, b'a', // codepoint + 0x00, 0x00, // top_left_x = 0 + 0xFF, 0xFD, // top_left_y = -3 + 0x00, 0x04, // size_x = 4 + 0x00, 0x04, // size_y = 4 + 0x00, 0x06, // device_width = 6 + 0x00, 0x00, 0x00, 0x00, // bitmap offset + + // 'b' glyph + 0x00, 0x00, 0x00, b'b', // codepoint + 0x00, 0x00, // top_left_x = 0 + 0xFF, 0xFC, // top_left_y = -4 + 0x00, 0x05, // size_x = 5 + 0x00, 0x05, // size_y = 5 + 0x00, 0x05, // device_width = 5 + 0x00, 0x00, 0x00, 4 * 4 / 8, // bitmap offset + + // glyph bitmaps + 0b11111001, 0b10011111, + 0b00001_000, 0b10_00100_0, 0b1000_1000, 0b0_0000000, + ]; + + #[test] + fn metadata() { + let font = SerializedBdfFont::new(TEST_FONT).unwrap(); + + assert_eq!( + font.metrics(), + Metrics { + ascent: 4, + descent: 1, + line_height: 5 + } + ); + + assert_eq!(font.glyph_count(), 2); + + assert!(font.lookup_by_index(0).is_some()); + assert!(font.lookup_by_index(1).is_some()); + assert!(font.lookup_by_index(2).is_none()); + + let _a = font.lookup('a').unwrap(); + let b = font.lookup('b').unwrap(); + assert_eq!(font.lookup('c'), None); + + let replacement = font.replacement_glyph(); + assert_eq!(replacement, Some(b)); + } + + fn draw_text(font: &SerializedBdfFont, text: &str) -> MockDisplay { + let text_style = SerializedBdfTextStyle::new(font, BinaryColor::On); + + let mut display = MockDisplay::new(); + Text::new(text, Point::new(0, 4), text_style) + .draw(&mut display) + .unwrap(); + + display + } + + #[test] + fn draw() { + let font = SerializedBdfFont::new(TEST_FONT).unwrap(); + draw_text(&font, "ab?").assert_pattern(&[ + " # #", + "#### # # ", + "# # # # ", + "# # # # ", + "#### # # ", + ]); + } + + #[test] + fn truncated_font() { + assert_eq!(SerializedBdfFont::new(&[]), Err("No header")); + assert_eq!( + SerializedBdfFont::new(&TEST_FONT[0..HEADER_SIZE]), + Err("No metadata") + ); + + // drawing a font without any glyph bitmap data shouldn't panic + let no_glyph_bitmaps_font = + SerializedBdfFont::new(&TEST_FONT[0..HEADER_SIZE + 2 * GLYPH_TABLE_ENTRY_SIZE]) + .unwrap(); + draw_text(&no_glyph_bitmaps_font, "abc").assert_pattern(&[]); + } +} diff --git a/eg-font-converter/src/serializer.rs b/eg-font-converter/src/serializer.rs index f1a128d..71cb330 100644 --- a/eg-font-converter/src/serializer.rs +++ b/eg-font-converter/src/serializer.rs @@ -25,7 +25,7 @@ pub fn serialize(font: BdfFont) -> anyhow::Result> { append_be_data!(glyph.bounding_box.top_left.y, i16); append_be_data!(glyph.bounding_box.size.width, u16); append_be_data!(glyph.bounding_box.size.height, u16); - append_be_data!(glyph.device_width, u8); + append_be_data!(glyph.device_width, u16); append_be_data!(glyph.start_index, u32); } diff --git a/eg-font-converter/tests/build.rs b/eg-font-converter/tests/build.rs index 65e4359..cce9ed8 100644 --- a/eg-font-converter/tests/build.rs +++ b/eg-font-converter/tests/build.rs @@ -36,12 +36,12 @@ fn assert_data(data: &[u8], expected: &[u8], width: u32) { #[test] fn eg_bdf_az() { - /* let font = FontConverter::with_file("../eg-bdf-examples/examples/6x10.bdf", "EG_BDF_AZ") + let font = FontConverter::with_file("../eg-bdf-examples/examples/6x10.bdf", "EG_BDF_AZ") .glyphs('a'..='z') .convert_eg_bdf() .unwrap(); - assert_eq!(font.rust(), include_str!("expected/eg_bdf_az.rs")); */ + assert_eq!(font.rust(), include_str!("expected/eg_bdf_az.rs")); } #[test] diff --git a/eg-font-converter/tests/expected/eg_bdf_az.rs b/eg-font-converter/tests/expected/eg_bdf_az.rs index a875c1a..2f1db25 100644 --- a/eg-font-converter/tests/expected/eg_bdf_az.rs +++ b/eg-font-converter/tests/expected/eg_bdf_az.rs @@ -26,151 +26,151 @@ pub const EG_BDF_AZ: ::eg_bdf::BdfFont = { character: 'b', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 60usize, + start_index: 8usize, }, ::eg_bdf::BdfGlyph { character: 'c', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 120usize, + start_index: 16usize, }, ::eg_bdf::BdfGlyph { character: 'd', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 180usize, + start_index: 24usize, }, ::eg_bdf::BdfGlyph { character: 'e', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 240usize, + start_index: 32usize, }, ::eg_bdf::BdfGlyph { character: 'f', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 300usize, + start_index: 40usize, }, ::eg_bdf::BdfGlyph { character: 'g', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 360usize, + start_index: 48usize, }, ::eg_bdf::BdfGlyph { character: 'h', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 420usize, + start_index: 56usize, }, ::eg_bdf::BdfGlyph { character: 'i', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 480usize, + start_index: 64usize, }, ::eg_bdf::BdfGlyph { character: 'j', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 540usize, + start_index: 72usize, }, ::eg_bdf::BdfGlyph { character: 'k', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 600usize, + start_index: 80usize, }, ::eg_bdf::BdfGlyph { character: 'l', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 660usize, + start_index: 88usize, }, ::eg_bdf::BdfGlyph { character: 'm', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 720usize, + start_index: 96usize, }, ::eg_bdf::BdfGlyph { character: 'n', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 780usize, + start_index: 104usize, }, ::eg_bdf::BdfGlyph { character: 'o', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 840usize, + start_index: 112usize, }, ::eg_bdf::BdfGlyph { character: 'p', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 900usize, + start_index: 120usize, }, ::eg_bdf::BdfGlyph { character: 'q', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 960usize, + start_index: 128usize, }, ::eg_bdf::BdfGlyph { character: 'r', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1020usize, + start_index: 136usize, }, ::eg_bdf::BdfGlyph { character: 's', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1080usize, + start_index: 144usize, }, ::eg_bdf::BdfGlyph { character: 't', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1140usize, + start_index: 152usize, }, ::eg_bdf::BdfGlyph { character: 'u', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1200usize, + start_index: 160usize, }, ::eg_bdf::BdfGlyph { character: 'v', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1260usize, + start_index: 168usize, }, ::eg_bdf::BdfGlyph { character: 'w', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1320usize, + start_index: 176usize, }, ::eg_bdf::BdfGlyph { character: 'x', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1380usize, + start_index: 184usize, }, ::eg_bdf::BdfGlyph { character: 'y', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1440usize, + start_index: 192usize, }, ::eg_bdf::BdfGlyph { character: 'z', bounding_box: rect(0i32, -7i32, 6u32, 10u32), device_width: 6u32, - start_index: 1500usize, + start_index: 200usize, }, ], }