1
use std::ops::Deref;
2
use std::sync::Arc;
3

            
4
use figures::{Displayable, Figure, Round};
5

            
6
use crate::color::Color;
7
use crate::math::{Pixels, Point};
8
use crate::scene::{Element, Target};
9
use crate::text::Font;
10

            
11
/// A vertical metrics measurement.
12
#[derive(Copy, Clone, Debug)]
13
pub struct VMetrics {
14
    /// The amount of pixels above the baseline.
15
    pub ascent: Figure<f32, Pixels>,
16
    /// The amount of pixels below the baseline. Typically a negative number.
17
    pub descent: Figure<f32, Pixels>,
18
    /// The amount of pixels to allow between lines.
19
    pub line_gap: Figure<f32, Pixels>,
20
}
21

            
22
impl From<rusttype::VMetrics> for VMetrics {
23
    fn from(value: rusttype::VMetrics) -> Self {
24
        Self {
25
            ascent: Figure::new(value.ascent),
26
            descent: Figure::new(value.descent),
27
            line_gap: Figure::new(value.line_gap),
28
        }
29
    }
30
}
31

            
32
impl VMetrics {
33
    /// The total height of the line.
34
    #[must_use]
35
    pub fn line_height(&self) -> Figure<f32, Pixels> {
36
        self.height() + self.line_gap
37
    }
38

            
39
    /// The height of the ascent and descent combined.
40
    #[must_use]
41
    pub fn height(&self) -> Figure<f32, Pixels> {
42
        self.ascent - self.descent
43
    }
44
}
45

            
46
/// A formatted span of text that is ready to render. Cheap to clone.
47
#[derive(Clone, Debug)]
48
pub struct PreparedSpan {
49
    /// The location of the span.
50
    pub location: Point<f32, Pixels>,
51
    data: Arc<PreparedSpanData>,
52
}
53

            
54
impl PreparedSpan {
55
    #[must_use]
56
    pub(crate) fn new(
57
        font: Font,
58
        size: Figure<f32, Pixels>,
59
        color: Color,
60
        width: Figure<f32, Pixels>,
61
        characters: Vec<char>,
62
        glyphs: Vec<GlyphInfo>,
63
        metrics: rusttype::VMetrics,
64
    ) -> Self {
65
        Self {
66
            location: Point::default(),
67
            data: Arc::new(PreparedSpanData {
68
                font,
69
                size,
70
                color,
71
                width,
72
                characters,
73
                glyphs,
74
                metrics,
75
            }),
76
        }
77
    }
78

            
79
    #[must_use]
80
    pub(crate) fn translate(&self, location: Point<f32, Pixels>) -> Self {
81
        Self {
82
            // We want to ensure that we are pixel-aligned when rendering a span's start.
83
            location: location.round(),
84
            data: self.data.clone(),
85
        }
86
    }
87

            
88
    /// Renders the text in `scene` with the baseline at `location`
89
    #[allow(clippy::needless_pass_by_value)]
90
    pub fn render_baseline_at(
91
        &self,
92
        scene: &Target,
93
        location: impl Displayable<f32, Pixels = Point<f32, Pixels>>,
94
    ) -> crate::Result<()> {
95
        let effective_scale_factor = scene.scale();
96

            
97
        let location =
98
            scene.offset_point_raw(location.to_pixels(effective_scale_factor) + self.location);
99
        scene.push_element(Element::Text {
100
            span: self.translate(location),
101
            clip: scene.clip,
102
        });
103
        Ok(())
104
    }
105
}
106

            
107
impl Deref for PreparedSpan {
108
    type Target = PreparedSpanData;
109

            
110
    fn deref(&self) -> &Self::Target {
111
        self.data.as_ref()
112
    }
113
}
114

            
115
/// The shared data of a [`PreparedSpan`].
116
#[derive(Debug)]
117
pub struct PreparedSpanData {
118
    /// The font being rendered.
119
    pub font: Font,
120
    /// The font size.
121
    pub size: Figure<f32, Pixels>,
122
    /// The color to render.
123
    pub color: Color,
124
    /// The total width of the span.
125
    pub width: Figure<f32, Pixels>,
126
    /// THe characters that compose this span.
127
    pub characters: Vec<char>,
128
    /// The glyphs that will be rendered.
129
    pub glyphs: Vec<GlyphInfo>,
130
    /// The vertical metrics of the span.
131
    pub metrics: rusttype::VMetrics,
132
}
133

            
134
/// Information about a font glyph
135
#[derive(Debug)]
136
pub struct GlyphInfo {
137
    /// The offset of the glyph within the source string.
138
    pub source_offset: usize,
139
    /// The character responsible for this glyph.
140
    pub source: char,
141
    /// The positioned glyph.
142
    pub glyph: rusttype::PositionedGlyph<'static>,
143
}
144

            
145
impl GlyphInfo {
146
    /// The width of the glyph.
147
    #[must_use]
148
    pub fn width(&self) -> Figure<f32, Pixels> {
149
        Figure::new(self.glyph.unpositioned().h_metrics().advance_width)
150
    }
151

            
152
    /// The location of the glyph, relative to the span start.
153
    #[must_use]
154
    pub fn location(&self) -> Point<f32, Pixels> {
155
        Point::new(self.glyph.position().x, self.glyph.position().y)
156
    }
157
}