1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Font face types

use crate::conv::{LineMetrics, DPU};
use crate::GlyphId;
use ttf_parser::Face;

/// Handle to a loaded font face
#[derive(Copy, Clone, Debug)]
pub struct FaceRef(pub(crate) &'static Face<'static>);

impl FaceRef {
    /// Get glyph identifier for a char
    ///
    /// If the char is not found, `GlyphId(0)` is returned (the 'missing glyph'
    /// representation).
    #[inline]
    pub fn glyph_index(&self, c: char) -> GlyphId {
        // GlyphId 0 is required to be a special glyph representing a missing
        // character (see cmap table / TrueType specification).
        GlyphId(self.0.glyph_index(c).map(|id| id.0).unwrap_or(0))
    }

    /// Convert `dpem` to `dpu`
    ///
    /// Output: a font-specific scale.
    ///
    /// Input: `dpem` is pixels/em
    ///
    /// ```none
    /// dpem
    ///   = pt_size × dpp
    ///   = pt_size × dpi / 72
    ///   = pt_size × scale_factor × (96 / 72)
    /// ```
    #[inline]
    pub fn dpu(self, dpem: f32) -> DPU {
        DPU(dpem / f32::from(self.0.units_per_em()))
    }

    /// Get a scaled reference
    ///
    /// Units: `dpem` is dots (pixels) per Em (module documentation).
    #[inline]
    pub fn scale_by_dpem(self, dpem: f32) -> ScaledFaceRef {
        ScaledFaceRef(self.0, self.dpu(dpem))
    }

    /// Get a scaled reference
    ///
    /// Units: `dpu` is dots (pixels) per font-unit (see module documentation).
    #[inline]
    pub fn scale_by_dpu(self, dpu: DPU) -> ScaledFaceRef {
        ScaledFaceRef(self.0, dpu)
    }

    /// Get the height of horizontal text in pixels
    ///
    /// Units: `dpem` is dots (pixels) per Em (module documentation).
    #[inline]
    pub fn height(&self, dpem: f32) -> f32 {
        self.scale_by_dpem(dpem).height()
    }
}

/// Handle to a loaded font face
#[derive(Copy, Clone, Debug)]
pub struct ScaledFaceRef(&'static Face<'static>, DPU);
impl ScaledFaceRef {
    /// Unscaled face
    #[inline]
    pub fn face(&self) -> FaceRef {
        FaceRef(self.0)
    }

    /// Scale
    #[inline]
    pub fn dpu(&self) -> DPU {
        self.1
    }

    /// Horizontal advancement after this glyph, without shaping or kerning
    #[inline]
    pub fn h_advance(&self, id: GlyphId) -> f32 {
        let x = self.0.glyph_hor_advance(id.into()).unwrap();
        self.1.u16_to_px(x)
    }

    /// Horizontal side bearing
    ///
    /// If unspecified by the font this resolves to 0.
    #[inline]
    pub fn h_side_bearing(&self, id: GlyphId) -> f32 {
        let x = self.0.glyph_hor_side_bearing(id.into()).unwrap_or(0);
        self.1.i16_to_px(x)
    }

    /// Ascender
    #[inline]
    pub fn ascent(&self) -> f32 {
        self.1.i16_to_px(self.0.ascender())
    }

    /// Descender
    #[inline]
    pub fn descent(&self) -> f32 {
        self.1.i16_to_px(self.0.descender())
    }

    /// Line gap
    #[inline]
    pub fn line_gap(&self) -> f32 {
        self.1.i16_to_px(self.0.line_gap())
    }

    /// Line height
    #[inline]
    pub fn height(&self) -> f32 {
        self.1.i16_to_px(self.0.height())
    }

    /// Metrics for underline
    #[inline]
    pub fn underline_metrics(&self) -> Option<LineMetrics> {
        self.0
            .underline_metrics()
            .map(|m| self.1.to_line_metrics(m))
    }

    /// Metrics for strike-through
    #[inline]
    pub fn strikethrough_metrics(&self) -> Option<LineMetrics> {
        self.0
            .strikeout_metrics()
            .map(|m| self.1.to_line_metrics(m))
    }
}