Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions bdf-parser/src/glyph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ impl Glyphs {
}
}

if glyphs.is_empty() {
return Err(ParserError::new("no CHARS in font"));
}

Ok(Self { glyphs })
}

Expand All @@ -315,6 +319,32 @@ impl Glyphs {
pub fn iter(&self) -> impl Iterator<Item = &Glyph> {
self.glyphs.iter()
}

/// Approximates the ascent.
///
/// See section 8.2.1 FONT_ASCENT in https://www.x.org/docs/XLFD/xlfd.pdf.
pub(crate) fn approximate_ascent(&self) -> u32 {
self.glyphs
.iter()
.map(|glyph| glyph.bounding_box.size.y - glyph.bounding_box.offset.y)
.max()
.unwrap_or_default()
.try_into()
.unwrap()
}

/// Approximates the descent.
///
/// See section 8.2.2 FONT_DESCENT in https://www.x.org/docs/XLFD/xlfd.pdf.
pub(crate) fn approximate_descent(&self) -> u32 {
self.glyphs
.iter()
.map(|glyph| -glyph.bounding_box.offset.y)
.max()
.unwrap_or_default()
.try_into()
.unwrap()
}
}

#[cfg(test)]
Expand Down
45 changes: 43 additions & 2 deletions bdf-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod properties;
pub use glyph::{Encoding, Glyph, Glyphs};
pub use metadata::{Metadata, MetricsSet};
pub use parser::ParserError;
pub use properties::{Properties, Property, PropertyError, PropertyType};
pub use properties::{Properties, Property, PropertyType};

use crate::parser::{Line, Lines};

Expand All @@ -24,6 +24,9 @@ pub struct Font {

/// Glyphs.
pub glyphs: Glyphs,

/// Metrics.
pub metrics: Metrics,
}

impl Font {
Expand All @@ -44,8 +47,13 @@ impl Font {

let metadata = Metadata::parse(&mut lines)?;
let glyphs = Glyphs::parse(&mut lines, &metadata)?;
let metrics = Metrics::new(&metadata, &glyphs)?;

Ok(Font { metadata, glyphs })
Ok(Font {
metadata,
glyphs,
metrics,
})
}
}

Expand Down Expand Up @@ -133,6 +141,39 @@ impl Coord {
}
}

/// Metrics.
#[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Metrics {
/// Ascent above the baseline in pixels.
pub ascent: u32,

/// Descent above the baseline in pixels.
pub descent: u32,
}

impl Metrics {
fn new(metadata: &Metadata, glyphs: &Glyphs) -> Result<Self, ParserError> {
let ascent = metadata
.properties
.try_get::<u32>(Property::FontAscent)
.map_err(|_| ParserError::new("invalid value for FONT_ASCENT property"))?
.unwrap_or_else(|| glyphs.approximate_ascent());

let descent = metadata
.properties
.try_get::<u32>(Property::FontDescent)
.map_err(|_| ParserError::new("invalid value for FONT_DESCENT property"))?
.unwrap_or_else(|| glyphs.approximate_descent());

Ok(Self { ascent, descent })
}

/// Gets the line height in pixels.
pub const fn line_height(&self) -> u32 {
self.ascent + self.descent
}
}

#[cfg(test)]
mod tests {
use crate::{glyph::GlyphWidth, properties::PropertyValue};
Expand Down
10 changes: 8 additions & 2 deletions bdf-parser/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ mod tests {
FONTBOUNDINGBOX 0 1 2 3
SIZE 1 2 3
COMMENT "comment"
CHARS 0
CHARS 1
STARTCHAR 0
BITMAP
ENDCHAR
ENDFONT
"#};

Expand Down Expand Up @@ -193,7 +196,10 @@ mod tests {
SIZE 1 2 3
COMMENT "comment"
METRICSSET 2
CHARS 0
CHARS 1
STARTCHAR 0
BITMAP
ENDCHAR
ENDFONT
"#};

Expand Down
77 changes: 46 additions & 31 deletions bdf-parser/src/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,25 @@ impl Properties {

/// Tries to get a property.
///
/// Returns an error if the property doesn't exist or the value has the wrong type.
pub fn try_get<T: PropertyType>(&self, property: Property) -> Result<T, PropertyError> {
/// Returns `None` if the property doesn't exits and an error if the value has the wrong type.
pub fn try_get<T: PropertyType>(
&self,
property: Property,
) -> Result<Option<T>, PropertyTypeError> {
self.try_get_by_name(&property.to_string())
}

/// Tries to get a property by name.
///
/// Returns an error if the property doesn't exist or the value has the wrong type.
pub fn try_get_by_name<T: PropertyType>(&self, name: &str) -> Result<T, PropertyError> {
/// Returns `None` if the property doesn't exits and an error if the value has the wrong type.
pub fn try_get_by_name<T: PropertyType>(
&self,
name: &str,
) -> Result<Option<T>, PropertyTypeError> {
self.properties
.get(name)
.ok_or_else(|| PropertyError::Undefined(name.to_string()))
.and_then(TryFrom::try_from)
.map(|value| value.try_into())
.transpose()
}

/// Returns `true` if no properties exist.
Expand All @@ -200,12 +206,13 @@ impl Properties {
/// Marker trait for property value types.
pub trait PropertyType
where
Self: for<'a> TryFrom<&'a PropertyValue, Error = PropertyError>,
Self: for<'a> TryFrom<&'a PropertyValue, Error = PropertyTypeError>,
{
}

impl PropertyType for String {}
impl PropertyType for i32 {}
impl PropertyType for u32 {}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PropertyValue {
Expand All @@ -214,38 +221,43 @@ pub enum PropertyValue {
}

impl TryFrom<&PropertyValue> for String {
type Error = PropertyError;
type Error = PropertyTypeError;

fn try_from(value: &PropertyValue) -> Result<Self, Self::Error> {
match value {
PropertyValue::Text(text) => Ok(text.clone()),
_ => Err(PropertyError::WrongType),
_ => Err(PropertyTypeError),
}
}
}

impl TryFrom<&PropertyValue> for i32 {
type Error = PropertyError;
type Error = PropertyTypeError;

fn try_from(value: &PropertyValue) -> Result<Self, Self::Error> {
match value {
PropertyValue::Int(int) => Ok(*int),
_ => Err(PropertyError::WrongType),
_ => Err(PropertyTypeError),
}
}
}

/// Error returned by property getters.
#[derive(Debug, Error, PartialEq, Eq, PartialOrd, Ord)]
pub enum PropertyError {
/// Undefined property.
#[error("property \"{0}\" is undefined")]
Undefined(String),
/// Wrong property type.
#[error("wrong property type")]
WrongType,
impl TryFrom<&PropertyValue> for u32 {
type Error = PropertyTypeError;

fn try_from(value: &PropertyValue) -> Result<Self, Self::Error> {
match value {
PropertyValue::Int(int) if *int >= 0 => Ok(*int as u32),
_ => Err(PropertyTypeError),
}
}
}

/// Invalid property type error.
#[derive(Debug, Error, PartialEq, Eq, PartialOrd, Ord)]
#[error("invalid property type")]
pub struct PropertyTypeError;

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -271,7 +283,7 @@ mod tests {
] {
assert_eq!(
properties.try_get_by_name::<String>(key).unwrap(),
expected.to_string(),
Some(expected.to_string()),
"key=\"{key}\""
);
}
Expand All @@ -289,16 +301,19 @@ mod tests {
let mut lines = Lines::new(INPUT);
let properties = Properties::parse(&mut lines).unwrap();

for (key, expected) in [
("POS_INT", 10), //
("NEG_INT", -20),
] {
assert_eq!(
properties.try_get_by_name::<i32>(key).unwrap(),
expected,
"key=\"{key}\""
);
}
assert_eq!(properties.try_get_by_name::<i32>("POS_INT"), Ok(Some(10)));
assert_eq!(properties.try_get_by_name::<i32>("NEG_INT"), Ok(Some(-20)));

assert_eq!(properties.try_get_by_name::<u32>("POS_INT"), Ok(Some(10)));
assert_eq!(
properties.try_get_by_name::<u32>("NEG_INT"),
Err(PropertyTypeError)
);

assert_eq!(
properties.try_get_by_name::<String>("POS_INT"),
Err(PropertyTypeError)
);
}

#[test]
Expand Down
11 changes: 5 additions & 6 deletions eg-bdf-examples/examples/font_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,26 @@ fn try_main() -> Result<()> {
.nth(1)
.ok_or_else(|| anyhow!("missing filename"))?;

let converted_font = FontConverter::with_file(&file, "BDF_FILE")
let converter = FontConverter::with_file(&file, "BDF_FILE")
.glyphs(Mapping::Ascii)
.missing_glyph_substitute('?');

let output = converted_font
let bdf_output = converter
.convert_eg_bdf()
.with_context(|| "couldn't convert font")?;
let bdf_font = output.as_font();
let bdf_font = bdf_output.as_font();

let output = converted_font
let mono_output = converter
.convert_mono_font()
.with_context(|| "couldn't convert font")?;
let mono_font = output.as_font();
let mono_font = mono_output.as_font();

let hints_style = MonoTextStyle::new(&FONT_6X10, Rgb888::CSS_DIM_GRAY);
let bottom_right = TextStyleBuilder::new()
.baseline(Baseline::Bottom)
.alignment(Alignment::Right)
.build();

// TODO: add metrics getter
let line_height = bdf_font.ascent + bdf_font.descent;
let display_height = line_height * 8;
let display_width = (line_height * 25).max(display_height);
Expand Down
17 changes: 11 additions & 6 deletions eg-font-converter/src/eg_bdf_font.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{fs, io, path::Path};

use anyhow::Result;
use bdf_parser::{BoundingBox, Encoding};
use bdf_parser::{BoundingBox, Encoding, Metrics};
use bitvec::{prelude::*, vec::BitVec};
use eg_bdf::{BdfFont, BdfGlyph};
use embedded_graphics::{
Expand Down Expand Up @@ -84,11 +84,14 @@ impl EgBdfOutput {
let constant_name = format_ident!("{}", self.font.name);
let data_file = self.font.data_file().to_string_lossy().to_string();
let ConvertedFont {
bdf,
replacement_character,
ascent,
descent,
..
} = self.font;
} = &self.font;

let Metrics {
ascent, descent, ..
} = bdf.metrics;

let glyphs = self.glyphs.iter().map(|glyph| {
let BdfGlyph {
Expand Down Expand Up @@ -150,10 +153,12 @@ impl EgBdfOutput {

/// Returns the converted font as a [`BdfFont`].
pub fn as_font(&self) -> BdfFont<'_> {
let metrics = &self.font.bdf.metrics;

BdfFont {
replacement_character: self.font.replacement_character,
ascent: self.font.ascent,
descent: self.font.descent,
ascent: metrics.ascent,
descent: metrics.descent,
glyphs: &self.glyphs,
data: self.data(),
}
Expand Down
Loading