word_cloud/words/
font.rs

1use std::{error::Error, fs, path::Path};
2
3use rusttype::{point, Scale, VMetrics};
4
5use crate::geometry::{Orientation, Rectangle};
6
7use super::{GlyphData, Word};
8
9pub struct Font<'font> {
10    font: Box<rusttype::Font<'font>>,
11}
12
13impl<'font> Font<'font> {
14    pub fn new(font_file: String) -> Result<Font<'font>, Box<dyn Error>> {
15        if !Path::new(&font_file).exists() {
16            return Err(format!("Font file not found: {}", font_file).into());
17        }
18
19        let content = fs::read(&font_file)?;
20
21        let Some(font) = rusttype::Font::try_from_vec(content) else {
22            return Err(format!("Cannot load font: {}", font_file).into());
23        };
24
25        Ok(Font {
26            font: Box::new(font),
27        })
28    }
29
30    pub fn get_metrics(&self, scale: Scale) -> VMetrics {
31        self.font.v_metrics(scale)
32    }
33
34    pub fn height(&self, scale: Scale) -> u32 {
35        let metrics = self.get_metrics(scale);
36        (metrics.ascent - metrics.descent).ceil() as u32
37    }
38
39    pub fn get_bounding_box(&self, word: &Word, scale: Scale) -> Rectangle {
40        let glyph_data = self.get_glyph_data(&word.text, scale);
41
42        match word.orientation {
43            Orientation::Horizontal => Rectangle::new(0, 0, glyph_data.width, glyph_data.height),
44            Orientation::Vertical => Rectangle::new(0, 0, glyph_data.height, glyph_data.width),
45        }
46    }
47
48    pub fn get_glyph_data(&self, text: &str, scale: Scale) -> GlyphData {
49        let glyphs: Vec<_> = self.font.layout(text, scale, point(0.0, 0.0)).collect();
50        let height = self.height(scale);
51        let width = {
52            let min_x = glyphs
53                .first()
54                .map(|g| g.pixel_bounding_box().unwrap().min.x)
55                .unwrap();
56            let max_x = glyphs
57                .last()
58                .map(|g| g.pixel_bounding_box().unwrap().max.x)
59                .unwrap();
60            (max_x - min_x) as u32
61        };
62
63        GlyphData {
64            width,
65            height,
66            glyphs,
67        }
68    }
69}