diff --git a/bdf-parser/src/glyph.rs b/bdf-parser/src/glyph.rs index 1ad0611..6006a51 100644 --- a/bdf-parser/src/glyph.rs +++ b/bdf-parser/src/glyph.rs @@ -10,6 +10,18 @@ use std::convert::TryFrom; use crate::{helpers::*, BoundingBox, Coord}; +/// Glyph encoding +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Encoding { + /// Standard encoding + Standard(u32), + /// Non standard encoding + NonStandard(u32), + /// Unspecified encoding + #[default] + Unspecified, +} + /// Glyph. #[derive(Debug, Clone, PartialEq, Default)] pub struct Glyph { @@ -17,7 +29,7 @@ pub struct Glyph { pub name: String, /// Encoding. - pub encoding: Option, + pub encoding: Encoding, /// Scalable width. pub scalable_width: Option, @@ -89,10 +101,19 @@ impl Glyph { } } -fn parse_encoding(input: &[u8]) -> IResult<&[u8], Option> { - map(parse_to_i32, |code| { - u32::try_from(code).ok().and_then(std::char::from_u32) - })(input) +fn parse_encoding(input: &[u8]) -> IResult<&[u8], Encoding> { + let (input, standard_encoding) = parse_to_i32(input)?; + let (input, non_standard_encoding) = opt(preceded(multispace0, parse_to_i32))(input)?; + + let encoding = if standard_encoding >= 0 { + Encoding::Standard(u32::try_from(standard_encoding).unwrap()) + } else if let Some(non_standard) = non_standard_encoding { + Encoding::NonStandard(u32::try_from(non_standard).unwrap()) + } else { + Encoding::Unspecified + }; + + Ok((input, encoding)) } fn parse_bitmap(input: &[u8]) -> IResult<&[u8], Vec> { @@ -136,8 +157,11 @@ impl Glyphs { /// Gets a glyph by the encoding. pub fn get(&self, c: char) -> Option<&Glyph> { + // TODO: this assumes that the font uses unicode + let encoding = Encoding::Standard(c as u32); + self.glyphs - .binary_search_by_key(&Some(c), |glyph| glyph.encoding) + .binary_search_by_key(&encoding, |glyph| glyph.encoding) .map_or(None, |i| Some(&self.glyphs[i])) } @@ -222,7 +246,7 @@ mod tests { "#}, Glyph { name: "ZZZZ".to_string(), - encoding: Some('A'), //65 + encoding: Encoding::Standard(65), // 'A' bitmap: vec![ 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, @@ -358,7 +382,35 @@ mod tests { size: Coord::new(0, 0), offset: Coord::new(0, 0), }, - encoding: None, + encoding: Encoding::Unspecified, + name: "000".to_string(), + scalable_width: Some(Coord::new(432, 0)), + device_width: Coord::new(6, 0), + } + ); + } + + #[test] + fn parse_glyph_with_no_encoding_and_index() { + let chardata = indoc! {br#" + STARTCHAR 000 + ENCODING -1 123 + SWIDTH 432 0 + DWIDTH 6 0 + BBX 0 0 0 0 + BITMAP + ENDCHAR + "#}; + + assert_parser_ok!( + Glyph::parse(chardata), + Glyph { + bitmap: vec![], + bounding_box: BoundingBox { + size: Coord::new(0, 0), + offset: Coord::new(0, 0), + }, + encoding: Encoding::NonStandard(123), name: "000".to_string(), scalable_width: Some(Coord::new(432, 0)), device_width: Coord::new(6, 0), @@ -386,7 +438,7 @@ mod tests { size: Coord::new(0, 0), offset: Coord::new(0, 0), }, - encoding: Some('\x00'), + encoding: Encoding::Standard(0), name: "000".to_string(), scalable_width: Some(Coord::new(432, 0)), device_width: Coord::new(6, 0), diff --git a/bdf-parser/src/lib.rs b/bdf-parser/src/lib.rs index e28699b..bdebf40 100644 --- a/bdf-parser/src/lib.rs +++ b/bdf-parser/src/lib.rs @@ -19,7 +19,7 @@ mod glyph; mod metadata; mod properties; -pub use glyph::{Glyph, Glyphs}; +pub use glyph::{Encoding, Glyph, Glyphs}; use helpers::*; pub use metadata::Metadata; pub use properties::{Properties, Property, PropertyError}; @@ -236,7 +236,7 @@ mod tests { size: Coord::new(8, 8), offset: Coord::new(0, 0), }, - encoding: Some('@'), //64 + encoding: Encoding::Standard(64), // '@' name: "Char 0".to_string(), device_width: Coord::new(8, 0), scalable_width: None, @@ -247,7 +247,7 @@ mod tests { size: Coord::new(8, 8), offset: Coord::new(0, 0), }, - encoding: Some('A'), //65 + encoding: Encoding::Standard(65), // 'A' name: "Char 1".to_string(), device_width: Coord::new(8, 0), scalable_width: None, diff --git a/eg-font-converter/src/eg_bdf_font.rs b/eg-font-converter/src/eg_bdf_font.rs index ecdd423..d51238c 100644 --- a/eg-font-converter/src/eg_bdf_font.rs +++ b/eg-font-converter/src/eg_bdf_font.rs @@ -1,7 +1,7 @@ use std::{fs, io, path::Path}; -use anyhow::Result; -use bdf_parser::BoundingBox; +use anyhow::{bail, Result}; +use bdf_parser::{BoundingBox, Encoding}; use bitvec::{prelude::*, vec::BitVec}; use eg_bdf::{BdfFont, BdfGlyph}; use embedded_graphics::{ @@ -44,8 +44,15 @@ impl EgBdfOutput { for glyph in font.glyphs.iter() { let bounding_box = bounding_box_to_rectangle(&glyph.bounding_box); + // TODO: assumes unicode + // TODO: improved error handling + let character = match glyph.encoding { + Encoding::Standard(index) => char::from_u32(index).unwrap(), + _ => bail!("invalid encoding"), + }; + glyphs.push(BdfGlyph { - character: glyph.encoding.unwrap(), + character, bounding_box, device_width: glyph.device_width.x as u32, // TODO: check cast and handle y? start_index: data.len(), diff --git a/eg-font-converter/src/lib.rs b/eg-font-converter/src/lib.rs index 30e8324..2dc8156 100644 --- a/eg-font-converter/src/lib.rs +++ b/eg-font-converter/src/lib.rs @@ -74,7 +74,7 @@ #![deny(unsafe_code)] use anyhow::{anyhow, ensure, Context, Result}; -use bdf_parser::{BdfFont as ParserBdfFont, Glyph, Property}; +use bdf_parser::{BdfFont as ParserBdfFont, Encoding, Glyph, Property}; use embedded_graphics::mono_font::mapping::GlyphMapping; use std::{ collections::BTreeSet, @@ -292,7 +292,8 @@ impl<'a> FontConverter<'a> { .cloned() .map(|mut glyph| { // replace glyph encoding for substitutes - glyph.encoding = Some(c); + // TODO: assumes unicode + glyph.encoding = Encoding::Standard(c as u32); glyph }) .ok_or_else(|| { @@ -412,10 +413,13 @@ struct Font { impl Font { fn glyph_index(&self, c: char) -> Option { + // TODO: assumes unicode + let encoding = Encoding::Standard(c as u32); + self.glyphs .iter() .enumerate() - .find(|(_, glyph)| glyph.encoding == Some(c)) + .find(|(_, glyph)| glyph.encoding == encoding) .map(|(index, _)| index) } @@ -441,10 +445,13 @@ impl Font { impl GlyphMapping for Font { fn index(&self, c: char) -> usize { + // TODO: assumes unicode + let encoding = Encoding::Standard(c as u32); + self.glyphs .iter() .enumerate() - .find(|(_, glyph)| glyph.encoding.unwrap() == c) + .find(|(_, glyph)| glyph.encoding == encoding) .map(|(index, _)| index) .unwrap() } @@ -594,6 +601,6 @@ mod tests { let converter = FontConverter::new_from_data(FONT, "TEST"); let font = converter.convert().unwrap(); assert_eq!(font.glyphs.len(), 1); - assert_eq!(font.glyphs[0].encoding, Some('\0')); + assert_eq!(font.glyphs[0].encoding, Encoding::Standard(0)); } } diff --git a/eg-font-converter/src/mono_font.rs b/eg-font-converter/src/mono_font.rs index e47061c..2a91f05 100644 --- a/eg-font-converter/src/mono_font.rs +++ b/eg-font-converter/src/mono_font.rs @@ -1,7 +1,7 @@ use std::{fs, io, ops::RangeInclusive, path::Path}; -use anyhow::{Context, Result}; -use bdf_parser::{BdfFont as ParserBdfFont, Glyph}; +use anyhow::{bail, Context, Result}; +use bdf_parser::{BdfFont as ParserBdfFont, Encoding, Glyph}; use eg_bdf::BdfTextStyle; use embedded_graphics::{ image::ImageRaw, @@ -75,14 +75,15 @@ impl MonoFontOutput { let x = (i % glyphs_per_row) as i32 * character_size.width as i32; let y = (i / glyphs_per_row) as i32 * character_size.height as i32; - Text::with_baseline( - &String::from(glyph.encoding.unwrap()), - Point::new(x, y), - style, - Baseline::Top, - ) - .draw(&mut bitmap) - .unwrap(); + // TODO: assumes unicode + let c = match glyph.encoding { + Encoding::Standard(index) => char::from_u32(index).unwrap(), + _ => bail!("invalid encoding"), + }; + + Text::with_baseline(&String::from(c), Point::new(x, y), style, Baseline::Top) + .draw(&mut bitmap) + .unwrap(); } let data = bitmap.to_be_bytes(); @@ -134,8 +135,15 @@ impl MonoFontOutput { let mime = format_ident!("{}", mapping.mime()); quote!(::embedded_graphics::mono_font::mapping::#mime) } else { - let str_mapping = - glyphs_to_str_mapping(self.font.glyphs.iter().map(|glyph| glyph.encoding.unwrap())); + let str_mapping = glyphs_to_str_mapping(self.font.glyphs.iter().map(|glyph| { + // TODO: assumes unicode + let c = match glyph.encoding { + Encoding::Standard(index) => char::from_u32(index).unwrap(), + _ => panic!("invalid encoding"), + }; + + c + })); let replacement = self.font.replacement_character; quote!(::embedded_graphics::mono_font::mapping::StrGlyphMapping::new(#str_mapping, #replacement)) }; @@ -246,7 +254,15 @@ fn glyphs_to_mapping(glyphs: &[Glyph]) -> Option { if glyphs .iter() - .map(|glyph| glyph.encoding.unwrap()) + .map(|glyph| { + // TODO: assumes unicode + let c = match glyph.encoding { + Encoding::Standard(index) => char::from_u32(index).unwrap(), + _ => panic!("invalid encoding"), + }; + + c + }) .eq(chars.iter().copied()) { return Some(mapping); @@ -293,7 +309,7 @@ mod tests { fn test_glyphs_to_mapping() { let glyphs = (' '..='\x7F') .map(|c| Glyph { - encoding: Some(c), + encoding: Encoding::Standard(c as u32), ..Glyph::default() }) .collect::>(); @@ -302,7 +318,7 @@ mod tests { let glyphs = (' '..='\x7F') .chain(0xA0 as char..=0xFF as char) .map(|c| Glyph { - encoding: Some(c), + encoding: Encoding::Standard(c as u32), ..Glyph::default() }) .collect::>();