use rusttype::{point, Scale, PositionedGlyph};
use image::{Rgba, ImageBuffer, imageops::flip_vertical};
pub struct Font<'a> {
font: rusttype::Font<'a>
}
impl<'a> Font<'a> {
pub fn new_ttf(path: &str) -> Result<Font, String> {
if let Ok(data) = std::fs::read(path) {
if let Some(font) = rusttype::Font::try_from_vec(data) {
return Ok(Font{font});
}
}
Err(format!("failed to open or parse font '{}'.", path))
}
pub fn snapshot(&self, text: &str, font_size: f32) -> Result<image::RgbaImage, String> {
let scale = Scale::uniform(font_size);
let v_metrics = self.font.v_metrics(scale);
let glyphs: Vec<PositionedGlyph> = self.font
.layout(&text, scale, point(0.0, 0.0 + v_metrics.ascent))
.collect();
let glyphs_height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
if let Some(glyphs_width) = glyph_width(&glyphs) {
let mut image = ImageBuffer::new(glyphs_width + 10, glyphs_height + 10);
for glyph in glyphs {
if let Some(bounding_box) = glyph.pixel_bounding_box() {
glyph.draw(|x,y,v| {
image.put_pixel(
x + bounding_box.min.x as u32,
y + bounding_box.min.y as u32,
Rgba([255, 255, 255, (v * 255.0) as u8]),
)
});
}
}
let image = flip_vertical(&image);
return Ok(image);
}
Err("could not calculate the glyphs width".to_string())
}
}
fn glyph_width(glyphs: &Vec<PositionedGlyph>) -> Option<u32> {
if let Some(first) = glyphs.first() {
if let Some(last) = glyphs.last() {
let max_x = last.pixel_bounding_box()?.max.x;
let min_x = first.pixel_bounding_box()?.min.x;
return Some((max_x - min_x) as u32);
}
}
None
}